package megadrum;

import megadrum.gui.*;
import megadrum.mididata.*;
import megadrum.util.*;
import megadrum.mdupdate.*;

import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Properties;
import javax.sound.midi.*;
import java.io.*;
import java.net.URL;
import javax.swing.plaf.metal.MetalLookAndFeel;
import com.jgoodies.looks.plastic.PlasticLookAndFeel;
import com.lipstikLF.LipstikLookAndFeel;
import net.infonode.gui.laf.InfoNodeLookAndFeel;
import com.nilo.plaf.nimrod.NimRODLookAndFeel;

public class MegadrumConfigTool extends javax.swing.JFrame
        implements ActionListener, ListSelectionListener, Receiver {

    public MegadrumConfigTool(boolean allowLoadingSettingsFromMD) {

        log.info("<font color=#8b84af>Initialization started</font>");

        readConfigurationFiles();

        updateLookAndFeel();

        initComponents();
        initComponents2();

        //center app window on screen and show
        setLocationRelativeTo(null);
        //repack the GUI
        pack();
        //maximize if it was maximized last session
        setExtendedState(windowState);
        //set visible
        setVisible(true);

        //if first run show the initial dialog
        firstRun();

        //if midiport.dll is not present install it
        if (Common.getOS() == Common.WINDOWS) {
            installMidiPort();
        }

        initializeMidiDevices();

        loadSession();

        //make all the actions work adding the listeners

        addActionListeners();
        addCloseListener();

        // Try updating if update on restart is set
        if (updateOnRestart) {
            BinFile b = BinFile.loadFile(this, "Select encrypted firmware file", "bin");
            if (b != null) {
                updateEncryptedFirmware(b);
            } else {
                updateOnRestart = false;
                updateConfigurationFile();
            }
        } else {
            // Otherwise continue normal execution

            //get firmware info and write it to file
            getFirmwareInfo(false); //don't show message box with version info

            //check for updates in the megadrum web server
            //checkForFirmwareUpdates(false); //don't show info message box if firmware is already up to date

            log.info("<font color=#8b84af>Initialization finished</font>");

            if (allowLoadingSettingsFromMD) {
                //try to read current megadrum settings from the device
                loadAllSettingsFromMD();
            }
        }
    }

    // ##################
    // ### INITIALIZE ###
    // ##################
    //read saved previous selections
    private void readConfigurationFiles() {
        //create dir structure if it has not been created yet
        File f = new File(Common.getUserProfileFolder());
        //if folder doesn't exists this may be the first run
        if (!f.exists()) {
            firstRun = true;
        } else {
            firstRun = false;
        }

        //create dirs
        f.mkdirs();

        FileInputStream inS = null;
        try {
            Properties p = new Properties();
            inS = new FileInputStream(Common.getUserProfileFolder() + Common.INI_FILE);
            p.load(inS);

            try {
                //we take into account that the hihatpedal is not a trigger input
                numberInputs = Integer.parseInt((String) (p.getProperty(Common.NUMBEROFINPUTS))) - 1; //32 - 1 = 31
            } catch (NumberFormatException n) {
                numberInputs = Common.DEFAULT_NUMBEROFINPUTS - 1;
            }

            try {
                chainID = Integer.parseInt((String) (p.getProperty(Common.CHAINID)));
            } catch (NumberFormatException n) {
                chainID = Common.DEFAULT_CHAINID;
            }

            try {
                bootloader = Integer.parseInt((String) (p.getProperty(Common.BOOTLOADER)));
            } catch (NumberFormatException n) {
                bootloader = Common.DEFAULT_BOOTLOADER;
            }

            try {
                lookandfeel = Integer.parseInt((String) (p.getProperty(Common.LOOKANDFEEL)));
            } catch (NumberFormatException n) {
                lookandfeel = Common.DEFAULT_LOOKANDFEEL;
            }

            try {
                theme = Integer.parseInt((String) (p.getProperty(Common.THEME)));
            } catch (NumberFormatException n) {
                theme = Common.DEFAULT_THEME;
            }

            try {
                rimSwitch = Integer.parseInt((String) (p.getProperty(Common.RIMSWITCH)));
            } catch (NumberFormatException n) {
                rimSwitch = Common.DEFAULT_RIMSWITCH_THRESHOLD;
            }

            try {
                sysexWaitTime = Integer.parseInt((String) (p.getProperty(Common.SYSEXWAITTIME)));
            } catch (NumberFormatException n) {
                sysexWaitTime = Common.DEFAULT_SYSEX_WAIT_TIME;
            }

            try {
                quickAccessPad = Integer.parseInt((String) (p.getProperty(Common.QUICKACCESS_PAD)));
            } catch (NumberFormatException n) {
                quickAccessPad = Common.DEFAULT_QUICKACCESS_PAD_THRESHOLD;
            }

            try {
                quickAccessPedal = Integer.parseInt((String) (p.getProperty(Common.QUICKACCESS_PEDAL)));
            } catch (NumberFormatException n) {
                quickAccessPedal = Common.DEFAULT_QUICKACCESS_PEDAL_THRESHOLD;
            }

            try {
                windowState = Integer.parseInt((String) (p.getProperty(Common.WINDOW_STATE)));
            } catch (NumberFormatException n) {
                windowState = Common.DEFAULT_WINDOWSTATE;
            }

            try {
                updateOnRestart = Boolean.parseBoolean((String) (p.getProperty(Common.UPDATE_ON_RESTART)));
            } catch (NumberFormatException n) {
                updateOnRestart = Common.DEFAULT_UPDATE_ON_RESTART;
            }

            MCU = (String) (p.getProperty(Common.MCU));
            if (MCU == null) {
                MCU = Common.DEFAULT_MCU;
            }

            clock = (String) (p.getProperty(Common.CLOCK));
            if (clock == null) {
                clock = Common.DEFAULT_CLOCK;
            }

            lastUpdate = (String) (p.getProperty(Common.LASTUPDATE));
            if (lastUpdate == null) {
                lastUpdate = Common.DEFAULT_LASTUPDATE;
            }

            liveEditingEnabled = Boolean.parseBoolean((String) (p.getProperty(Common.LIVEEDITING_ENABLE)));
            loadAtStartupEnabled = Boolean.parseBoolean((String) (p.getProperty(Common.LOADATSTARTUP_ENABLE)));
            quickAccessEnabled = Boolean.parseBoolean((String) (p.getProperty(Common.QUICKACCESS_ENABLE)));

            inputDeviceName = (String) (p.getProperty(Common.MIDIIN));

            outputDeviceName = (String) (p.getProperty(Common.MIDIOUT));

            throughDeviceName = (String) (p.getProperty(Common.MIDITHROUGH));

            inS.close();
            log.info("Configuration file read successfully");

        } catch (Exception e) {
            try {
                inS.close();
            } catch (Exception e2) {
            }
            //default config if any exception is thrown
            log.warn("Reading configuration file. Trying now to write default values...");
            setDefaultConfig();
            updateConfigurationFile();
        }

        //Now we have read the number of inputs, calculate the number of mono and stereo pads
        calculateNumberOfPads();

        // Load pad names from properties file
        padName = new String[numberMonoPads + numberStereoPads];

        try {
            Properties p = new Properties();
            inS = new FileInputStream(Common.getUserProfileFolder() + Common.PAD_TABLE_FILE);
            p.load(inS);
            for (int i = 0; i < Common.MAXIMUM_NUMBER_OF_PADS; i++) {
                setPadName(i, (String) (p.getProperty("Pad" + (i + 1))));
            }
            updatePadListNames();
            inS.close();
            log.info("Pad names file read successfully");
        } catch (Exception e) {
            try {
                inS.close();
            } catch (Exception e2) {
            }
            log.warn("Reading pad names file. Trying now to write default values...");
            //default config if any exception is thrown
            setDefaultPadNames();
            updatePadNamesFile();
        }
        // Load input names from properties file
        inputName = new String[numberInputs];

        try {
            Properties p = new Properties();
            inS = new FileInputStream(Common.getUserProfileFolder() + Common.SUBPAD_TABLE_FILE);
            p.load(inS);
            for (int i = 0; i < Common.MAXIMUM_NUMBER_OF_SUBPADS; i++) {
                setInputName(i, (String) (p.getProperty("Input" + (i + 1))));
            }
            inS.close();
            log.info("Input names file read successfully");
        } catch (Exception e) {
            try {
                inS.close();
            } catch (Exception e2) {
            }
            log.warn("Reading input names file. Trying now to write default values...");
            //default config if any exception is thrown
            setDefaultInputNames();
            updateInputNamesFile();
        }
    }

    private void setDefaultConfig() {
        inputDeviceName = null;
        outputDeviceName = null;
        throughDeviceName = null;
        //we take into account that the hihatpedal is not a trigger input
        numberInputs = Common.DEFAULT_NUMBEROFINPUTS - 1; //32 - 1 = 31 trigger inputs (minus hihat pedal)
        MCU = Common.DEFAULT_MCU;
        clock = Common.DEFAULT_CLOCK;
        bootloader = Common.DEFAULT_BOOTLOADER;
        updateOnRestart = Common.DEFAULT_UPDATE_ON_RESTART;
        chainID = Common.DEFAULT_CHAINID;
        lookandfeel = Common.DEFAULT_LOOKANDFEEL;
        theme = Common.DEFAULT_THEME;
        rimSwitch = Common.DEFAULT_RIMSWITCH_THRESHOLD;
        sysexWaitTime = Common.DEFAULT_SYSEX_WAIT_TIME;
        windowState = Common.DEFAULT_WINDOWSTATE;
        liveEditingEnabled = Common.DEFAULT_LIVEEDITING;
        loadAtStartupEnabled = Common.DEFAULT_LOADATSTARTUP;
        quickAccessEnabled = Common.DEFAULT_QUICKACCESS;
        quickAccessPad = Common.DEFAULT_QUICKACCESS_PAD_THRESHOLD;
        quickAccessPedal = Common.DEFAULT_QUICKACCESS_PEDAL_THRESHOLD;
        lastUpdate = Common.DEFAULT_LASTUPDATE;
    }

    public void updateConfigurationFile() {
        FileOutputStream outS = null;
        try {
            Properties p = new Properties();
            p.setProperty(Common.NUMBEROFINPUTS, "" + (numberInputs + 1));
            p.setProperty(Common.MCU, MCU);
            p.setProperty(Common.CLOCK, clock);
            p.setProperty(Common.BOOTLOADER, "" + bootloader);
            p.setProperty(Common.UPDATE_ON_RESTART, updateOnRestart ? "true" : "false");
            if (inputDeviceName != null) {
                p.setProperty(Common.MIDIIN, inputDeviceName);
            }
            if (outputDeviceName != null) {
                p.setProperty(Common.MIDIOUT, outputDeviceName);
            }
            if (throughDeviceName != null) {
                p.setProperty(Common.MIDITHROUGH, throughDeviceName);
            }
            p.setProperty(Common.CHAINID, "" + chainID);
            p.setProperty(Common.LOOKANDFEEL, "" + lookandfeel);
            p.setProperty(Common.THEME, "" + theme);
            p.setProperty(Common.RIMSWITCH, "" + rimSwitch);
            p.setProperty(Common.SYSEXWAITTIME, "" + sysexWaitTime);
            p.setProperty(Common.WINDOW_STATE, "" + windowState);
            p.setProperty(Common.LIVEEDITING_ENABLE, liveEditingEnabled ? "true" : "false");
            p.setProperty(Common.LOADATSTARTUP_ENABLE, loadAtStartupEnabled ? "true" : "false");
            p.setProperty(Common.QUICKACCESS_ENABLE, quickAccessEnabled ? "true" : "false");
            p.setProperty(Common.QUICKACCESS_PAD, "" + quickAccessPad);
            p.setProperty(Common.QUICKACCESS_PEDAL, "" + quickAccessPedal);
            p.setProperty(Common.LASTUPDATE, lastUpdate);
            outS = new FileOutputStream(Common.getUserProfileFolder() + Common.INI_FILE);
            p.store(outS, "\"Megadrum Config Tool\" parameters");
            outS.close();
            log.info("Configuration file saved to " + Common.getUserProfileFolder() + Common.INI_FILE);
        } catch (Exception e) {
            try {
                outS.close();
            } catch (Exception e2) {
            }
            log.error("Trying to save configuration file<br>The error was: " + Log.getExceptionInfo(e));
        }
    }

    private void setDefaultPadNames() {
        for (int i = 0; i < padName.length; i++) {
            padName[i] = Common.PAD_NAMES[i];
        }
    }

    private void setDefaultInputNames() {
        for (int i = 0; i < inputName.length; i++) {
            inputName[i] = Common.SUBPAD_NAMES[i];
        }
    }

    public void updatePadNamesFile() {
        FileOutputStream outS = null;
        try {
            Properties p = new Properties();
            for (int i = 0; i < padName.length; i++) {
                p.setProperty("Pad" + (i + 1), padName[i]);
            }
            //if number of pads has been increased, write default values for the additional subpad pads
            for (int i = padName.length; i < numberMonoPads + numberStereoPads; i++) {
                p.setProperty("Pad" + (i + 1), Common.SUBPAD_NAMES[i]);
            }
            outS = new FileOutputStream(Common.getUserProfileFolder() + Common.PAD_TABLE_FILE);
            p.store(outS, "\"Megadrum Config Tool\" pad names");
            outS.close();
            log.info("Pad names file saved to " + Common.getUserProfileFolder() + Common.PAD_TABLE_FILE);
        } catch (Exception e) {
            try {
                outS.close();
            } catch (Exception e2) {
            }
            log.error("Trying to save pad names file<br>The error was: " + Log.getExceptionInfo(e));
        }
    }

    public void updateInputNamesFile() {
        FileOutputStream outS = null;
        try {
            Properties p = new Properties();
            for (int i = 0; i < inputName.length; i++) {
                p.setProperty("Input" + (i + 1), inputName[i]);
            }
            //if number of inputs has been increased, write default values for the additional subpad inputs
            for (int i = inputName.length; i < numberInputs; i++) {
                p.setProperty("Input" + (i + 1), Common.SUBPAD_NAMES[i]);
            }
            outS = new FileOutputStream(Common.getUserProfileFolder() + Common.SUBPAD_TABLE_FILE);
            p.store(outS, "\"Megadrum Config Tool\" input names");
            outS.close();
            log.info("Input names file saved to " + Common.getUserProfileFolder() + Common.SUBPAD_TABLE_FILE);
        } catch (Exception e) {
            try {
                outS.close();
            } catch (Exception e2) {
            }
            log.error("Trying to save input names file<br>The error was: " + Log.getExceptionInfo(e));
        }
    }

    //set number of inputs and pads for later configuration of all GUI interface
    private void calculateNumberOfPads() {
        //number of stereo and mono pads
        numberMonoPads = Common.NUMBER_OF_MONO_PADS;
        numberStereoPads = (int) ((numberInputs - Common.NUMBER_OF_MONO_PADS) / 2);
    }

    //set and update look & feel with the selected theme
    public void updateLookAndFeel() {
        try {
            switch (lookandfeel) {
                case 0:
                    PlasticLookAndFeel.setCurrentTheme((theme >= 0 && theme < Common.METALTHEMES.length) ? Common.METALTHEMES[theme] : Common.METALTHEMES[0]);
                    UIManager.setLookAndFeel("com.jgoodies.looks.plastic.Plastic3DLookAndFeel");
                    break;
                case 1:
                    PlasticLookAndFeel.setCurrentTheme((theme >= 0 && theme < Common.METALTHEMES.length) ? Common.METALTHEMES[theme] : Common.METALTHEMES[0]);
                    UIManager.setLookAndFeel("com.jgoodies.looks.plastic.PlasticXPLookAndFeel");
                    break;
                case 2:
                    PlasticLookAndFeel.setCurrentTheme((theme >= 0 && theme < Common.METALTHEMES.length) ? Common.METALTHEMES[theme] : Common.METALTHEMES[0]);
                    UIManager.setLookAndFeel("com.jgoodies.looks.plastic.PlasticLookAndFeel");
                    break;
                case 3:
                    MetalLookAndFeel.setCurrentTheme((theme >= 0 && theme < Common.METALTHEMES.length) ? Common.METALTHEMES[theme] : Common.METALTHEMES[0]);
                    UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel");
                    break;
                case 4:
                    NimRODLookAndFeel laf = new NimRODLookAndFeel();
                    NimRODLookAndFeel.setCurrentTheme((theme >= 0 && theme < Common.NIMRODTHEMES.length) ? Common.NIMRODTHEMES[theme] : Common.NIMRODTHEMES[0]);
                    UIManager.setLookAndFeel(laf);
                    break;
                case 5:
                    LipstikLookAndFeel.setMyCurrentTheme((theme >= 0 && theme < Common.LIPSTIKTHEMES.length) ? Common.LIPSTIKTHEMES[theme] : Common.LIPSTIKTHEMES[0]);
                    UIManager.setLookAndFeel(new com.lipstikLF.LipstikLookAndFeel());
                    break;
                case 6:
                    UIManager.setLookAndFeel(new InfoNodeLookAndFeel((theme >= 0 && theme < Common.INFONODETHEMES.length) ? Common.INFONODETHEMES[theme] : Common.INFONODETHEMES[0]));
                    break;
            }
        } catch (Exception ex) {
        }
        //UPDATE GUI
        SwingUtilities.updateComponentTreeUI(this);
        if (midiLog != null) {
            SwingUtilities.updateComponentTreeUI(midiLog);
        }
        if (log != null) {
            SwingUtilities.updateComponentTreeUI(log);
        }
        if (pad != null) {
            for (int i = 0; i < pad.length; i++) {
                SwingUtilities.updateComponentTreeUI(pad[i]);
            }
        }
    }

    //initialize components (1)
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        javax.swing.JScrollPane jScrollPane1 = new javax.swing.JScrollPane();
        applicationPanel = new javax.swing.JPanel();
        javax.swing.JToolBar jToolBar2 = new javax.swing.JToolBar();
        javax.swing.JToolBar.Separator jSeparator8 = new javax.swing.JToolBar.Separator();
        javax.swing.JLabel jLabel7 = new javax.swing.JLabel();
        javax.swing.JToolBar.Separator jSeparator9 = new javax.swing.JToolBar.Separator();
        javax.swing.JLabel jLabel8 = new javax.swing.JLabel();
        b_loadMiscFromMega1 = new javax.swing.JButton();
        b_sendMiscToMega1 = new javax.swing.JButton();
        javax.swing.JToolBar.Separator jSeparator10 = new javax.swing.JToolBar.Separator();
        javax.swing.JLabel jLabel9 = new javax.swing.JLabel();
        b_loadHihatFromMega1 = new javax.swing.JButton();
        b_sendHihatToMega1 = new javax.swing.JButton();
        javax.swing.JToolBar.Separator jSeparator11 = new javax.swing.JToolBar.Separator();
        javax.swing.JLabel jLabel10 = new javax.swing.JLabel();
        b_loadPadFromMega1 = new javax.swing.JButton();
        b_sendPadToMega1 = new javax.swing.JButton();
        javax.swing.JToolBar.Separator jSeparator12 = new javax.swing.JToolBar.Separator();
        javax.swing.JLabel jLabel11 = new javax.swing.JLabel();
        b_loadFromMega1 = new javax.swing.JButton();
        b_sendToMega1 = new javax.swing.JButton();
        b_sendToMegaOrderWrite1 = new javax.swing.JButton();
        b_orderWrite = new javax.swing.JButton();
        javax.swing.JToolBar.Separator jSeparator13 = new javax.swing.JToolBar.Separator();
        progressBar = new javax.swing.JProgressBar();
        javax.swing.JToolBar.Separator jSeparator14 = new javax.swing.JToolBar.Separator();
        logoContainer = new javax.swing.JPanel();
        javax.swing.JLabel jLabel1 = new javax.swing.JLabel();
        miscPanelContainer = new javax.swing.JPanel();
        midiBarsContainer = new javax.swing.JPanel();
        hihatPanelContainer = new javax.swing.JPanel();
        javax.swing.JPanel padBigPanel = new javax.swing.JPanel();
        padList = new javax.swing.JList();
        javax.swing.JPanel parameterListPanel = new javax.swing.JPanel();
        parameterLabels = new javax.swing.JPanel();
        graphicalAidPanel = new javax.swing.JPanel();
        javax.swing.JScrollPane scrollPane = new javax.swing.JScrollPane();
        infoTextArea = new javax.swing.JTextArea();
        padPanelContainer = new javax.swing.JPanel();
        javax.swing.JPanel setAllPanel = new javax.swing.JPanel();
        note = new javax.swing.JSpinner();
        channel = new javax.swing.JSpinner();
        comprLvl = new javax.swing.JSpinner();
        lvlShift = new javax.swing.JSpinner();
        xtalk = new javax.swing.JSpinner();
        xtalkGrp = new javax.swing.JSpinner();
        threshold = new javax.swing.JSpinner();
        gain = new javax.swing.JSpinner();
        hiLvlAuto = new javax.swing.JSpinner();
        highLvl = new javax.swing.JSpinner();
        retrigger = new javax.swing.JSpinner();
        dynLevel = new javax.swing.JSpinner();
        dynTime = new javax.swing.JSpinner();
        minScan = new javax.swing.JSpinner();
        curve = new javax.swing.JSpinner();
        b_note = new javax.swing.JButton();
        b_channel = new javax.swing.JButton();
        b_curve = new javax.swing.JButton();
        b_comprLvl = new javax.swing.JButton();
        b_lvlShift = new javax.swing.JButton();
        b_xtalk = new javax.swing.JButton();
        b_xtalkGrp = new javax.swing.JButton();
        b_threshold = new javax.swing.JButton();
        b_gain = new javax.swing.JButton();
        b_hiLvlAuto = new javax.swing.JButton();
        b_highLvl = new javax.swing.JButton();
        b_retrigger = new javax.swing.JButton();
        b_dynLevel = new javax.swing.JButton();
        b_dynTime = new javax.swing.JButton();
        b_minScan = new javax.swing.JButton();
        dualHead = new javax.swing.JSpinner();
        b_dualHead = new javax.swing.JButton();
        bNote = new javax.swing.JSpinner();
        bThreshold = new javax.swing.JSpinner();
        b_bNote = new javax.swing.JButton();
        b_bThreshold = new javax.swing.JButton();
        taskPanel = new javax.swing.JPanel();
        javax.swing.JMenuBar menu = new javax.swing.JMenuBar();
        javax.swing.JMenu supermenu_actions = new javax.swing.JMenu();
        javax.swing.JMenu supermenu_all = new javax.swing.JMenu();
        menu_sendToMega = new javax.swing.JMenuItem();
        menu_sendToMegaOrderWrite = new javax.swing.JMenuItem();
        menu_loadFromMega = new javax.swing.JMenuItem();
        menu_saveToFile = new javax.swing.JMenuItem();
        menu_loadFromFile = new javax.swing.JMenuItem();
        javax.swing.JMenu supermenu_misc = new javax.swing.JMenu();
        menu_sendMiscToMega = new javax.swing.JMenuItem();
        menu_loadMiscFromMega = new javax.swing.JMenuItem();
        menu_saveMiscToFile = new javax.swing.JMenuItem();
        menu_loadMiscFromFile = new javax.swing.JMenuItem();
        javax.swing.JMenu supermenu_hihat = new javax.swing.JMenu();
        menu_sendHihatToMega = new javax.swing.JMenuItem();
        menu_loadHihatFromMega = new javax.swing.JMenuItem();
        menu_saveHihatToFile = new javax.swing.JMenuItem();
        menu_loadHihatFromFile = new javax.swing.JMenuItem();
        javax.swing.JMenu supermenu_pad = new javax.swing.JMenu();
        menu_sendPadToMega = new javax.swing.JMenuItem();
        menu_loadPadFromMega = new javax.swing.JMenuItem();
        menu_savePadToFile = new javax.swing.JMenuItem();
        menu_loadPadFromFile = new javax.swing.JMenuItem();
        javax.swing.JMenu supermenu_drummap = new javax.swing.JMenu();
        menu_saveDrumMap = new javax.swing.JMenuItem();
        menu_loadDrumMap = new javax.swing.JMenuItem();
        javax.swing.JSeparator separator1 = new javax.swing.JSeparator();
        menu_orderWrite = new javax.swing.JMenuItem();
        javax.swing.JSeparator separator2 = new javax.swing.JSeparator();
        menu_exit = new javax.swing.JMenuItem();
        supermenu_drummaps = new javax.swing.JMenu();
        menu_drummapEditor = new javax.swing.JMenuItem();
        menu_aftertouchMapping = new javax.swing.JMenuItem();
        menu_customCurves = new javax.swing.JMenuItem();
        supermenu_firmware = new javax.swing.JMenu();
        menu_updateFirmware = new javax.swing.JMenuItem();
        menu_inputNames = new javax.swing.JMenuItem();
        menu_modifyFirmware = new javax.swing.JMenuItem();
        menu_checkUpdate = new javax.swing.JMenuItem();
        supermenu_view = new javax.swing.JMenu();
        menu_midiLog = new javax.swing.JMenuItem();
        menu_appLog = new javax.swing.JMenuItem();
        javax.swing.JMenu supermenu_options = new javax.swing.JMenu();
        menu_options = new javax.swing.JMenuItem();
        javax.swing.JMenu supermenu_help = new javax.swing.JMenu();
        menu_help = new javax.swing.JMenuItem();
        menu_recommendedFirmware = new javax.swing.JMenuItem();
        javax.swing.JSeparator separator4 = new javax.swing.JSeparator();
        menu_about = new javax.swing.JMenuItem();

        setDefaultCloseOperation(javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE);

        jScrollPane1.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
        jScrollPane1.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);

        jToolBar2.setFloatable(false);
        jToolBar2.setRollover(true);
        jToolBar2.add(jSeparator8);

        jLabel7.setText("Load from/Send to MegaDrum >");
        jToolBar2.add(jLabel7);
        jToolBar2.add(jSeparator9);

        jLabel8.setText("-Misc:");
        jToolBar2.add(jLabel8);

        b_loadMiscFromMega1.setIcon(new javax.swing.ImageIcon(getClass().getResource("/megadrum/icons/load.png"))); // NOI18N
        b_loadMiscFromMega1.setToolTipText("Load miscellaneous settings from Megadrum");
        b_loadMiscFromMega1.setFocusable(false);
        jToolBar2.add(b_loadMiscFromMega1);

        b_sendMiscToMega1.setIcon(new javax.swing.ImageIcon(getClass().getResource("/megadrum/icons/save.png"))); // NOI18N
        b_sendMiscToMega1.setToolTipText("Send miscellaneous settings to Megadrum");
        b_sendMiscToMega1.setFocusable(false);
        jToolBar2.add(b_sendMiscToMega1);
        jToolBar2.add(jSeparator10);

        jLabel9.setText("-Hihat:");
        jToolBar2.add(jLabel9);

        b_loadHihatFromMega1.setIcon(new javax.swing.ImageIcon(getClass().getResource("/megadrum/icons/load.png"))); // NOI18N
        b_loadHihatFromMega1.setToolTipText("Load hihat settings from Megadrum");
        b_loadHihatFromMega1.setFocusable(false);
        jToolBar2.add(b_loadHihatFromMega1);

        b_sendHihatToMega1.setIcon(new javax.swing.ImageIcon(getClass().getResource("/megadrum/icons/save.png"))); // NOI18N
        b_sendHihatToMega1.setToolTipText("Send hihat settings to Megadrum");
        b_sendHihatToMega1.setFocusable(false);
        jToolBar2.add(b_sendHihatToMega1);
        jToolBar2.add(jSeparator11);

        jLabel10.setText("-Selected Pad:");
        jToolBar2.add(jLabel10);

        b_loadPadFromMega1.setIcon(new javax.swing.ImageIcon(getClass().getResource("/megadrum/icons/load.png"))); // NOI18N
        b_loadPadFromMega1.setToolTipText("Load selected pad settings from Megadrum");
        jToolBar2.add(b_loadPadFromMega1);

        b_sendPadToMega1.setIcon(new javax.swing.ImageIcon(getClass().getResource("/megadrum/icons/save.png"))); // NOI18N
        b_sendPadToMega1.setToolTipText("Send selected pad settings to Megadrum");
        jToolBar2.add(b_sendPadToMega1);
        jToolBar2.add(jSeparator12);

        jLabel11.setText("-All settings:");
        jToolBar2.add(jLabel11);

        b_loadFromMega1.setIcon(new javax.swing.ImageIcon(getClass().getResource("/megadrum/icons/load.png"))); // NOI18N
        b_loadFromMega1.setToolTipText("Load all settings from Megadrum");
        jToolBar2.add(b_loadFromMega1);

        b_sendToMega1.setIcon(new javax.swing.ImageIcon(getClass().getResource("/megadrum/icons/save.png"))); // NOI18N
        b_sendToMega1.setToolTipText("Send all settings to Megadrum");
        jToolBar2.add(b_sendToMega1);

        b_sendToMegaOrderWrite1.setIcon(new javax.swing.ImageIcon(getClass().getResource("/megadrum/icons/save+eeprom.png"))); // NOI18N
        b_sendToMegaOrderWrite1.setToolTipText("Send all settings to Megadrum and save them into Slot 1");
        jToolBar2.add(b_sendToMegaOrderWrite1);

        b_orderWrite.setIcon(new javax.swing.ImageIcon(getClass().getResource("/megadrum/icons/eeprom.png"))); // NOI18N
        b_orderWrite.setText("Write in Slot 1");
        b_orderWrite.setToolTipText("<html>&nbsp;\nRequest Megadrum to write&nbsp;<br>&nbsp;\nits current settings to Slot 1&nbsp;<br>&nbsp;\nof persistent memory&nbsp;");
        jToolBar2.add(b_orderWrite);

        jSeparator13.setSeparatorSize(new java.awt.Dimension(60, 10));
        jToolBar2.add(jSeparator13);

        progressBar.setString("");
        progressBar.setStringPainted(true);
        jToolBar2.add(progressBar);

        jSeparator14.setSeparatorSize(new java.awt.Dimension(20, 10));
        jToolBar2.add(jSeparator14);

        logoContainer.setLayout(new java.awt.GridLayout(1, 0));

        jLabel1.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
        jLabel1.setIcon(new javax.swing.ImageIcon(getClass().getResource("/megadrum/icons/logo.png"))); // NOI18N
        logoContainer.add(jLabel1);

        javax.swing.GroupLayout miscPanelContainerLayout = new javax.swing.GroupLayout(miscPanelContainer);
        miscPanelContainer.setLayout(miscPanelContainerLayout);
        miscPanelContainerLayout.setHorizontalGroup(
            miscPanelContainerLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 203, Short.MAX_VALUE)
        );
        miscPanelContainerLayout.setVerticalGroup(
            miscPanelContainerLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 170, Short.MAX_VALUE)
        );

        javax.swing.GroupLayout midiBarsContainerLayout = new javax.swing.GroupLayout(midiBarsContainer);
        midiBarsContainer.setLayout(midiBarsContainerLayout);
        midiBarsContainerLayout.setHorizontalGroup(
            midiBarsContainerLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 203, Short.MAX_VALUE)
        );
        midiBarsContainerLayout.setVerticalGroup(
            midiBarsContainerLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 349, Short.MAX_VALUE)
        );

        javax.swing.GroupLayout hihatPanelContainerLayout = new javax.swing.GroupLayout(hihatPanelContainer);
        hihatPanelContainer.setLayout(hihatPanelContainerLayout);
        hihatPanelContainerLayout.setHorizontalGroup(
            hihatPanelContainerLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 202, Short.MAX_VALUE)
        );
        hihatPanelContainerLayout.setVerticalGroup(
            hihatPanelContainerLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 602, Short.MAX_VALUE)
        );

        padBigPanel.setBorder(javax.swing.BorderFactory.createTitledBorder("Pads"));

        padList.setToolTipText("Select pad to configure");

        parameterListPanel.setBorder(javax.swing.BorderFactory.createTitledBorder("Parameters"));

        parameterLabels.setToolTipText("Pad's parameter list");
        parameterLabels.setLayout(new java.awt.GridLayout(16, 1, 0, 2));

        javax.swing.GroupLayout parameterListPanelLayout = new javax.swing.GroupLayout(parameterListPanel);
        parameterListPanel.setLayout(parameterListPanelLayout);
        parameterListPanelLayout.setHorizontalGroup(
            parameterListPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(parameterListPanelLayout.createSequentialGroup()
                .addContainerGap()
                .addComponent(parameterLabels, javax.swing.GroupLayout.DEFAULT_SIZE, 86, Short.MAX_VALUE)
                .addContainerGap())
        );
        parameterListPanelLayout.setVerticalGroup(
            parameterListPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(parameterLabels, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 423, Short.MAX_VALUE)
        );

        graphicalAidPanel.setBorder(javax.swing.BorderFactory.createEtchedBorder());

        scrollPane.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
        scrollPane.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);

        infoTextArea.setColumns(20);
        infoTextArea.setLineWrap(true);
        infoTextArea.setRows(5);
        infoTextArea.setText("Firmware Update Status:\n- Old bootloaders\n(v1 & v2):\n=> Fully Working\n- New bootloaders\n(v3 & v4):\n=> May work or not. Try by yourself.\nv5 (encrypted firmware version):\n=> Testing required.");
        infoTextArea.setWrapStyleWord(true);
        scrollPane.setViewportView(infoTextArea);

        javax.swing.GroupLayout graphicalAidPanelLayout = new javax.swing.GroupLayout(graphicalAidPanel);
        graphicalAidPanel.setLayout(graphicalAidPanelLayout);
        graphicalAidPanelLayout.setHorizontalGroup(
            graphicalAidPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(graphicalAidPanelLayout.createSequentialGroup()
                .addContainerGap()
                .addComponent(scrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 141, Short.MAX_VALUE)
                .addContainerGap())
        );
        graphicalAidPanelLayout.setVerticalGroup(
            graphicalAidPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(graphicalAidPanelLayout.createSequentialGroup()
                .addContainerGap()
                .addComponent(scrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 85, Short.MAX_VALUE)
                .addContainerGap())
        );

        javax.swing.GroupLayout padPanelContainerLayout = new javax.swing.GroupLayout(padPanelContainer);
        padPanelContainer.setLayout(padPanelContainerLayout);
        padPanelContainerLayout.setHorizontalGroup(
            padPanelContainerLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 234, Short.MAX_VALUE)
        );
        padPanelContainerLayout.setVerticalGroup(
            padPanelContainerLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 535, Short.MAX_VALUE)
        );

        setAllPanel.setBorder(javax.swing.BorderFactory.createTitledBorder("Set all pads"));

        note.setMaximumSize(new java.awt.Dimension(65, 20));
        note.setMinimumSize(new java.awt.Dimension(65, 20));

        channel.setMaximumSize(new java.awt.Dimension(65, 20));
        channel.setMinimumSize(new java.awt.Dimension(65, 20));

        comprLvl.setMaximumSize(new java.awt.Dimension(65, 20));
        comprLvl.setMinimumSize(new java.awt.Dimension(65, 20));

        lvlShift.setMaximumSize(new java.awt.Dimension(65, 20));
        lvlShift.setMinimumSize(new java.awt.Dimension(65, 20));

        xtalk.setMaximumSize(new java.awt.Dimension(65, 20));
        xtalk.setMinimumSize(new java.awt.Dimension(65, 20));

        xtalkGrp.setMaximumSize(new java.awt.Dimension(65, 20));
        xtalkGrp.setMinimumSize(new java.awt.Dimension(65, 20));

        threshold.setMaximumSize(new java.awt.Dimension(65, 20));
        threshold.setMinimumSize(new java.awt.Dimension(65, 20));

        gain.setMaximumSize(new java.awt.Dimension(65, 20));
        gain.setMinimumSize(new java.awt.Dimension(65, 20));

        hiLvlAuto.setMaximumSize(new java.awt.Dimension(65, 20));
        hiLvlAuto.setMinimumSize(new java.awt.Dimension(65, 20));

        highLvl.setMaximumSize(new java.awt.Dimension(65, 20));
        highLvl.setMinimumSize(new java.awt.Dimension(65, 20));

        retrigger.setMaximumSize(new java.awt.Dimension(65, 20));
        retrigger.setMinimumSize(new java.awt.Dimension(65, 20));

        dynLevel.setMaximumSize(new java.awt.Dimension(65, 20));
        dynLevel.setMinimumSize(new java.awt.Dimension(65, 20));

        dynTime.setMaximumSize(new java.awt.Dimension(65, 20));
        dynTime.setMinimumSize(new java.awt.Dimension(65, 20));

        minScan.setMaximumSize(new java.awt.Dimension(65, 20));
        minScan.setMinimumSize(new java.awt.Dimension(65, 20));

        curve.setMaximumSize(new java.awt.Dimension(65, 20));
        curve.setMinimumSize(new java.awt.Dimension(65, 20));

        b_note.setIcon(new javax.swing.ImageIcon(getClass().getResource("/megadrum/icons/left.png"))); // NOI18N
        b_note.setToolTipText("Set this Midi Note Number for all inputs");
        b_note.setMaximumSize(new java.awt.Dimension(30, 20));
        b_note.setMinimumSize(new java.awt.Dimension(30, 20));

        b_channel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/megadrum/icons/left.png"))); // NOI18N
        b_channel.setToolTipText("Set this Midi Channel for all inputs");
        b_channel.setMaximumSize(new java.awt.Dimension(30, 20));
        b_channel.setMinimumSize(new java.awt.Dimension(30, 20));

        b_curve.setIcon(new javax.swing.ImageIcon(getClass().getResource("/megadrum/icons/left.png"))); // NOI18N
        b_curve.setToolTipText("Set this Curve for all inputs");
        b_curve.setMaximumSize(new java.awt.Dimension(30, 20));
        b_curve.setMinimumSize(new java.awt.Dimension(30, 20));

        b_comprLvl.setIcon(new javax.swing.ImageIcon(getClass().getResource("/megadrum/icons/left.png"))); // NOI18N
        b_comprLvl.setToolTipText("Set this Compression Level for all inputs");
        b_comprLvl.setMaximumSize(new java.awt.Dimension(30, 20));
        b_comprLvl.setMinimumSize(new java.awt.Dimension(30, 20));

        b_lvlShift.setIcon(new javax.swing.ImageIcon(getClass().getResource("/megadrum/icons/left.png"))); // NOI18N
        b_lvlShift.setToolTipText("Set this Level Shift for all inputs");
        b_lvlShift.setMaximumSize(new java.awt.Dimension(30, 20));
        b_lvlShift.setMinimumSize(new java.awt.Dimension(30, 20));

        b_xtalk.setIcon(new javax.swing.ImageIcon(getClass().getResource("/megadrum/icons/left.png"))); // NOI18N
        b_xtalk.setToolTipText("Set this Crosstalk for all inputsl");
        b_xtalk.setMaximumSize(new java.awt.Dimension(30, 20));
        b_xtalk.setMinimumSize(new java.awt.Dimension(30, 20));

        b_xtalkGrp.setIcon(new javax.swing.ImageIcon(getClass().getResource("/megadrum/icons/left.png"))); // NOI18N
        b_xtalkGrp.setToolTipText("Set this Crosstalk Group for all inputs");
        b_xtalkGrp.setMaximumSize(new java.awt.Dimension(30, 20));
        b_xtalkGrp.setMinimumSize(new java.awt.Dimension(30, 20));

        b_threshold.setIcon(new javax.swing.ImageIcon(getClass().getResource("/megadrum/icons/left.png"))); // NOI18N
        b_threshold.setToolTipText("Set this Threshold for all inputs");
        b_threshold.setMaximumSize(new java.awt.Dimension(30, 20));
        b_threshold.setMinimumSize(new java.awt.Dimension(30, 20));

        b_gain.setIcon(new javax.swing.ImageIcon(getClass().getResource("/megadrum/icons/left.png"))); // NOI18N
        b_gain.setToolTipText("Set this Gain for all inputs");
        b_gain.setMaximumSize(new java.awt.Dimension(30, 20));
        b_gain.setMinimumSize(new java.awt.Dimension(30, 20));

        b_hiLvlAuto.setIcon(new javax.swing.ImageIcon(getClass().getResource("/megadrum/icons/left.png"))); // NOI18N
        b_hiLvlAuto.setToolTipText("Set this High Level Auto for all inputs");
        b_hiLvlAuto.setMaximumSize(new java.awt.Dimension(30, 20));
        b_hiLvlAuto.setMinimumSize(new java.awt.Dimension(30, 20));

        b_highLvl.setIcon(new javax.swing.ImageIcon(getClass().getResource("/megadrum/icons/left.png"))); // NOI18N
        b_highLvl.setToolTipText("Set this High Level for all inputs");
        b_highLvl.setMaximumSize(new java.awt.Dimension(30, 20));
        b_highLvl.setMinimumSize(new java.awt.Dimension(30, 20));

        b_retrigger.setIcon(new javax.swing.ImageIcon(getClass().getResource("/megadrum/icons/left.png"))); // NOI18N
        b_retrigger.setToolTipText("Set this Retrigger for all inputs");
        b_retrigger.setMaximumSize(new java.awt.Dimension(30, 20));
        b_retrigger.setMinimumSize(new java.awt.Dimension(30, 20));

        b_dynLevel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/megadrum/icons/left.png"))); // NOI18N
        b_dynLevel.setToolTipText("Set this Dynamic Thr Level for all inputs");
        b_dynLevel.setMaximumSize(new java.awt.Dimension(30, 20));
        b_dynLevel.setMinimumSize(new java.awt.Dimension(30, 20));

        b_dynTime.setIcon(new javax.swing.ImageIcon(getClass().getResource("/megadrum/icons/left.png"))); // NOI18N
        b_dynTime.setToolTipText("Set this Dynamic Time for all inputs");
        b_dynTime.setMaximumSize(new java.awt.Dimension(30, 20));
        b_dynTime.setMinimumSize(new java.awt.Dimension(30, 20));

        b_minScan.setIcon(new javax.swing.ImageIcon(getClass().getResource("/megadrum/icons/left.png"))); // NOI18N
        b_minScan.setToolTipText("Set this Min Scan Time for all inputs");
        b_minScan.setMaximumSize(new java.awt.Dimension(30, 20));
        b_minScan.setMinimumSize(new java.awt.Dimension(30, 20));

        dualHead.setMaximumSize(new java.awt.Dimension(65, 20));
        dualHead.setMinimumSize(new java.awt.Dimension(65, 20));

        b_dualHead.setIcon(new javax.swing.ImageIcon(getClass().getResource("/megadrum/icons/left.png"))); // NOI18N
        b_dualHead.setToolTipText("Set this Pad Type for all inputs");
        b_dualHead.setMaximumSize(new java.awt.Dimension(30, 20));
        b_dualHead.setMinimumSize(new java.awt.Dimension(30, 20));

        bNote.setMaximumSize(new java.awt.Dimension(65, 20));
        bNote.setMinimumSize(new java.awt.Dimension(65, 20));

        bThreshold.setMaximumSize(new java.awt.Dimension(65, 20));
        bThreshold.setMinimumSize(new java.awt.Dimension(65, 20));

        b_bNote.setIcon(new javax.swing.ImageIcon(getClass().getResource("/megadrum/icons/left.png"))); // NOI18N
        b_bNote.setToolTipText("Set this Bell Note for all inputs");
        b_bNote.setMaximumSize(new java.awt.Dimension(30, 20));
        b_bNote.setMinimumSize(new java.awt.Dimension(30, 20));

        b_bThreshold.setIcon(new javax.swing.ImageIcon(getClass().getResource("/megadrum/icons/left.png"))); // NOI18N
        b_bThreshold.setToolTipText("Set this Bell Threshold for all inputs");
        b_bThreshold.setMaximumSize(new java.awt.Dimension(30, 20));
        b_bThreshold.setMinimumSize(new java.awt.Dimension(30, 20));

        javax.swing.GroupLayout setAllPanelLayout = new javax.swing.GroupLayout(setAllPanel);
        setAllPanel.setLayout(setAllPanelLayout);
        setAllPanelLayout.setHorizontalGroup(
            setAllPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(setAllPanelLayout.createSequentialGroup()
                .addContainerGap()
                .addGroup(setAllPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(b_note, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, 30, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(b_channel, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, 30, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(b_curve, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, 30, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(b_comprLvl, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, 30, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(b_lvlShift, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, 30, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(b_xtalk, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, 30, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(b_xtalkGrp, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, 30, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(b_threshold, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, 30, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(b_gain, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, 30, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(b_hiLvlAuto, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, 30, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(b_highLvl, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, 30, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(b_retrigger, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, 30, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(b_dynLevel, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, 30, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(b_dynTime, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, 30, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(b_minScan, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, 30, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(b_dualHead, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, 30, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(setAllPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
                    .addComponent(note, javax.swing.GroupLayout.PREFERRED_SIZE, 65, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(channel, javax.swing.GroupLayout.PREFERRED_SIZE, 65, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(curve, javax.swing.GroupLayout.PREFERRED_SIZE, 65, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(comprLvl, javax.swing.GroupLayout.PREFERRED_SIZE, 65, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(lvlShift, javax.swing.GroupLayout.PREFERRED_SIZE, 65, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(xtalk, javax.swing.GroupLayout.PREFERRED_SIZE, 65, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(xtalkGrp, javax.swing.GroupLayout.PREFERRED_SIZE, 65, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(threshold, javax.swing.GroupLayout.PREFERRED_SIZE, 65, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(gain, javax.swing.GroupLayout.PREFERRED_SIZE, 65, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(hiLvlAuto, javax.swing.GroupLayout.PREFERRED_SIZE, 65, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(highLvl, javax.swing.GroupLayout.PREFERRED_SIZE, 65, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(retrigger, javax.swing.GroupLayout.PREFERRED_SIZE, 65, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(dynLevel, javax.swing.GroupLayout.PREFERRED_SIZE, 65, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(dynTime, javax.swing.GroupLayout.PREFERRED_SIZE, 65, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(minScan, javax.swing.GroupLayout.PREFERRED_SIZE, 65, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(dualHead, javax.swing.GroupLayout.PREFERRED_SIZE, 65, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, setAllPanelLayout.createSequentialGroup()
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .addGroup(setAllPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, setAllPanelLayout.createSequentialGroup()
                        .addComponent(b_bThreshold, javax.swing.GroupLayout.PREFERRED_SIZE, 30, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(bThreshold, javax.swing.GroupLayout.PREFERRED_SIZE, 65, javax.swing.GroupLayout.PREFERRED_SIZE))
                    .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, setAllPanelLayout.createSequentialGroup()
                        .addComponent(b_bNote, javax.swing.GroupLayout.PREFERRED_SIZE, 30, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(bNote, javax.swing.GroupLayout.PREFERRED_SIZE, 65, javax.swing.GroupLayout.PREFERRED_SIZE)))
                .addContainerGap())
        );
        setAllPanelLayout.setVerticalGroup(
            setAllPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(setAllPanelLayout.createSequentialGroup()
                .addGroup(setAllPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(note, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(b_note, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(setAllPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(channel, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(b_channel, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(setAllPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(curve, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(b_curve, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(setAllPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(comprLvl, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(b_comprLvl, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(setAllPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(lvlShift, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(b_lvlShift, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(setAllPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(xtalk, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(b_xtalk, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(setAllPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(xtalkGrp, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(b_xtalkGrp, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(setAllPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(threshold, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(b_threshold, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(setAllPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(gain, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(b_gain, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(setAllPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(hiLvlAuto, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(b_hiLvlAuto, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(setAllPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(highLvl, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(b_highLvl, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(setAllPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(retrigger, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(b_retrigger, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(setAllPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(dynLevel, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(b_dynLevel, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(setAllPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(dynTime, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(b_dynTime, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(setAllPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(b_minScan, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(minScan, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(setAllPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(dualHead, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(b_dualHead, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addGap(43, 43, 43)
                .addGroup(setAllPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                    .addComponent(bNote, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(b_bNote, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(setAllPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                    .addComponent(bThreshold, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(b_bThreshold, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addContainerGap())
        );

        javax.swing.GroupLayout taskPanelLayout = new javax.swing.GroupLayout(taskPanel);
        taskPanel.setLayout(taskPanelLayout);
        taskPanelLayout.setHorizontalGroup(
            taskPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 370, Short.MAX_VALUE)
        );
        taskPanelLayout.setVerticalGroup(
            taskPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 21, Short.MAX_VALUE)
        );

        javax.swing.GroupLayout padBigPanelLayout = new javax.swing.GroupLayout(padBigPanel);
        padBigPanel.setLayout(padBigPanelLayout);
        padBigPanelLayout.setHorizontalGroup(
            padBigPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(padBigPanelLayout.createSequentialGroup()
                .addContainerGap()
                .addGroup(padBigPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(padBigPanelLayout.createSequentialGroup()
                        .addComponent(padList, javax.swing.GroupLayout.PREFERRED_SIZE, 43, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(parameterListPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                    .addComponent(graphicalAidPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(padBigPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(padBigPanelLayout.createSequentialGroup()
                        .addComponent(padPanelContainer, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(setAllPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                    .addComponent(taskPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addContainerGap())
        );
        padBigPanelLayout.setVerticalGroup(
            padBigPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(padBigPanelLayout.createSequentialGroup()
                .addGroup(padBigPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
                    .addGroup(javax.swing.GroupLayout.Alignment.LEADING, padBigPanelLayout.createSequentialGroup()
                        .addGroup(padBigPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
                            .addComponent(padList, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                            .addComponent(parameterListPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(graphicalAidPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                    .addGroup(javax.swing.GroupLayout.Alignment.LEADING, padBigPanelLayout.createSequentialGroup()
                        .addGroup(padBigPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
                            .addComponent(setAllPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                            .addComponent(padPanelContainer, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(taskPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
                .addContainerGap())
        );

        javax.swing.GroupLayout applicationPanelLayout = new javax.swing.GroupLayout(applicationPanel);
        applicationPanel.setLayout(applicationPanelLayout);
        applicationPanelLayout.setHorizontalGroup(
            applicationPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(applicationPanelLayout.createSequentialGroup()
                .addGroup(applicationPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(applicationPanelLayout.createSequentialGroup()
                        .addContainerGap()
                        .addGroup(applicationPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
                            .addComponent(logoContainer, javax.swing.GroupLayout.DEFAULT_SIZE, 203, Short.MAX_VALUE)
                            .addComponent(miscPanelContainer, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                            .addComponent(midiBarsContainer, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(hihatPanelContainer, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(padBigPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                    .addComponent(jToolBar2, javax.swing.GroupLayout.DEFAULT_SIZE, 1012, Short.MAX_VALUE))
                .addContainerGap())
        );
        applicationPanelLayout.setVerticalGroup(
            applicationPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(applicationPanelLayout.createSequentialGroup()
                .addComponent(jToolBar2, javax.swing.GroupLayout.PREFERRED_SIZE, 25, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(applicationPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(applicationPanelLayout.createSequentialGroup()
                        .addComponent(logoContainer, javax.swing.GroupLayout.PREFERRED_SIZE, 71, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(miscPanelContainer, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(midiBarsContainer, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                    .addComponent(hihatPanelContainer, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(padBigPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addContainerGap())
        );

        jScrollPane1.setViewportView(applicationPanel);

        supermenu_actions.setText("Actions");

        supermenu_all.setText("All Megadrum settings");

        menu_sendToMega.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_F5, 0));
        menu_sendToMega.setText("Send to MegaDrum");
        supermenu_all.add(menu_sendToMega);

        menu_sendToMegaOrderWrite.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_F5, java.awt.event.InputEvent.SHIFT_MASK));
        menu_sendToMegaOrderWrite.setText("Send to MegaDrum and Write in Slot 1");
        supermenu_all.add(menu_sendToMegaOrderWrite);

        menu_loadFromMega.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_F5, java.awt.event.InputEvent.CTRL_MASK));
        menu_loadFromMega.setText("Load from MegaDrum");
        supermenu_all.add(menu_loadFromMega);

        menu_saveToFile.setText("Save to file");
        supermenu_all.add(menu_saveToFile);

        menu_loadFromFile.setText("Load from file");
        supermenu_all.add(menu_loadFromFile);

        supermenu_actions.add(supermenu_all);

        supermenu_misc.setText("Miscellaneous settings");

        menu_sendMiscToMega.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_F6, 0));
        menu_sendMiscToMega.setText("Send to MegaDrum");
        supermenu_misc.add(menu_sendMiscToMega);

        menu_loadMiscFromMega.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_F6, java.awt.event.InputEvent.CTRL_MASK));
        menu_loadMiscFromMega.setText("Load from MegaDrum");
        supermenu_misc.add(menu_loadMiscFromMega);

        menu_saveMiscToFile.setText("Save to file");
        supermenu_misc.add(menu_saveMiscToFile);

        menu_loadMiscFromFile.setText("Load from file");
        supermenu_misc.add(menu_loadMiscFromFile);

        supermenu_actions.add(supermenu_misc);

        supermenu_hihat.setText("Hihat Pedal settings");

        menu_sendHihatToMega.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_F7, 0));
        menu_sendHihatToMega.setText("Send to MegaDrum");
        supermenu_hihat.add(menu_sendHihatToMega);

        menu_loadHihatFromMega.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_F7, java.awt.event.InputEvent.CTRL_MASK));
        menu_loadHihatFromMega.setText("Load from MegaDrum");
        supermenu_hihat.add(menu_loadHihatFromMega);

        menu_saveHihatToFile.setText("Save to file");
        supermenu_hihat.add(menu_saveHihatToFile);

        menu_loadHihatFromFile.setText("Load from file");
        supermenu_hihat.add(menu_loadHihatFromFile);

        supermenu_actions.add(supermenu_hihat);

        supermenu_pad.setText("Selected Pad settings");

        menu_sendPadToMega.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_F8, 0));
        menu_sendPadToMega.setText("Send to MegaDrum");
        supermenu_pad.add(menu_sendPadToMega);

        menu_loadPadFromMega.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_F8, java.awt.event.InputEvent.CTRL_MASK));
        menu_loadPadFromMega.setText("Load from MegaDrum");
        supermenu_pad.add(menu_loadPadFromMega);

        menu_savePadToFile.setText("Save to file");
        supermenu_pad.add(menu_savePadToFile);

        menu_loadPadFromFile.setText("Load from file");
        supermenu_pad.add(menu_loadPadFromFile);

        supermenu_actions.add(supermenu_pad);

        supermenu_drummap.setText("Drum Map");

        menu_saveDrumMap.setText("Save to file");
        supermenu_drummap.add(menu_saveDrumMap);

        menu_loadDrumMap.setText("Load from file");
        supermenu_drummap.add(menu_loadDrumMap);

        supermenu_actions.add(supermenu_drummap);
        supermenu_actions.add(separator1);

        menu_orderWrite.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_ENTER, java.awt.event.InputEvent.CTRL_MASK));
        menu_orderWrite.setText("Write config present in MD into Slot 1");
        supermenu_actions.add(menu_orderWrite);
        supermenu_actions.add(separator2);

        menu_exit.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_Q, java.awt.event.InputEvent.CTRL_MASK));
        menu_exit.setText("Exit");
        supermenu_actions.add(menu_exit);

        menu.add(supermenu_actions);

        supermenu_drummaps.setText("Drummaps");

        menu_drummapEditor.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_D, java.awt.event.InputEvent.CTRL_MASK));
        menu_drummapEditor.setText("Drummap Editor");
        supermenu_drummaps.add(menu_drummapEditor);

        menu_aftertouchMapping.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_A, java.awt.event.InputEvent.CTRL_MASK));
        menu_aftertouchMapping.setText("Aftertouch Mapping");
        supermenu_drummaps.add(menu_aftertouchMapping);

        menu_customCurves.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_S, java.awt.event.InputEvent.CTRL_MASK));
        menu_customCurves.setText("Custom curves editor");
        supermenu_drummaps.add(menu_customCurves);

        menu.add(supermenu_drummaps);

        supermenu_firmware.setText("Firmware");

        menu_updateFirmware.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_U, java.awt.event.InputEvent.CTRL_MASK));
        menu_updateFirmware.setText("UPDATE FIRMWARE");
        supermenu_firmware.add(menu_updateFirmware);

        menu_inputNames.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_I, java.awt.event.InputEvent.CTRL_MASK));
        menu_inputNames.setText("Edit pad and input names");
        supermenu_firmware.add(menu_inputNames);

        menu_modifyFirmware.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_J, java.awt.event.InputEvent.CTRL_MASK));
        menu_modifyFirmware.setText("Modify firmware file with custom input names");
        supermenu_firmware.add(menu_modifyFirmware);

        menu_checkUpdate.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_K, java.awt.event.InputEvent.CTRL_MASK));
        menu_checkUpdate.setText("Check for updates");
        supermenu_firmware.add(menu_checkUpdate);

        menu.add(supermenu_firmware);

        supermenu_view.setText("View");

        menu_midiLog.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_M, java.awt.event.InputEvent.CTRL_MASK));
        menu_midiLog.setText("Midi Log");
        supermenu_view.add(menu_midiLog);

        menu_appLog.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_L, java.awt.event.InputEvent.CTRL_MASK));
        menu_appLog.setText("Application Log");
        supermenu_view.add(menu_appLog);

        menu.add(supermenu_view);

        supermenu_options.setText("Options");

        menu_options.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_O, java.awt.event.InputEvent.CTRL_MASK));
        menu_options.setText("Configure...");
        supermenu_options.add(menu_options);

        menu.add(supermenu_options);

        supermenu_help.setText("Help");

        menu_help.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_F1, 0));
        menu_help.setText("Help");
        supermenu_help.add(menu_help);

        menu_recommendedFirmware.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_F2, 0));
        menu_recommendedFirmware.setText("Recommended Firmware");
        supermenu_help.add(menu_recommendedFirmware);
        supermenu_help.add(separator4);

        menu_about.setText("About...");
        supermenu_help.add(menu_about);

        menu.add(supermenu_help);

        setJMenuBar(menu);

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(jScrollPane1)
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(jScrollPane1)
        );

        pack();
    }// </editor-fold>//GEN-END:initComponents

    //initialize components (2)
    private void initComponents2() {
        setTitle("MegaDrum Config Tool " + Common.VERSION);
        setIconImage((new ImageIcon(getClass().getResource("/megadrum/icons/icon.png"))).getImage());

        //initialize timers
        progressBarTimer = new Timer(Common.PROGRESSBAR_TIMER_TIME, this);
        progressBarTimer.setRepeats(false);
        liveEditingTimer = new Timer(Common.LIVEEDITING_TIMER_TIME, this);
        liveEditingTimer.setRepeats(false);

        //add labels for pad parameters
        for (int i = 0; i < Common.NUMBER_OF_SUBPAD_PARAMETERS; i++) {
            parameterLabels.add(new JLabel(Common.SUBPAD_PARAMETER_NAMES[i], JLabel.RIGHT));
        }

        //initialize data classes
        /*String[] s = new String[numberMonoPads + numberStereoPads];
        for (int i = 0; i < s.length; i++) {
        s[i] = Common.PAD_NAMES[i];
        }*/
        padList.setListData(padName);

        //create pad panels
        pad = new PadPanel[numberMonoPads + numberStereoPads];
        //create firstly the mono inputs
        for (int i = 0; i < numberMonoPads; i++) {
            pad[i] = new PadPanel(this, Common.MONO_PAD, i + 1); //kick is input 1
            pad[i].setSubpadName(Common.MONO, inputName[i]);
        }
        //second, create stereo inputs
        for (int i = numberMonoPads; i < pad.length; i++) { //input numbers passed to the constructor are 2,4,6,8...
            pad[i] = new PadPanel(this, Common.STEREO_PAD, i * 2 - numberMonoPads + 1);
            pad[i].setSubpadName(Common.HEAD, inputName[i * 2 - numberMonoPads]);
            pad[i].setSubpadName(Common.RIM, inputName[i * 2 - numberMonoPads + 1]);
        }

        //create hihat panel, and misc panel, and midi panels
        hihat = new HihatPanel(this);
        misc = new MiscPanel(this);
        midiLog = new MidiLog(this);
        midiMonitor = new MidiMonitor(this, midiLog);
        customCurves = new CustomCurvesEditor(this);
        drummapEditor = new DrummapEditor(this);
        aftertouchMapping = new AftertouchMapping(this);

        //init starting pedal position
        pedalPosition = 0;

        //add panels to GUI
        hihatPanelContainer.setLayout(new java.awt.BorderLayout());
        hihatPanelContainer.add(hihat, "Center");

        padPanelContainer.setLayout(new java.awt.BorderLayout());
        padPanelContainer.add(pad[0], "Center");
        padList.setSelectedIndex(0); //select Kick pad

        miscPanelContainer.setLayout(new java.awt.BorderLayout());
        miscPanelContainer.add(misc, "Center");

        midiBarsContainer.setLayout(new java.awt.BorderLayout());
        midiBarsContainer.add(midiMonitor, "Center");

        //set data models
        note.setModel(new CustomSpinnerNoteModel(0, 0, 127, 1));
        channel.setModel(new CustomSpinnerIntModel(10, 1, 16, 1));
        curve.setModel(new CustomSpinnerListModel(Common.CURVES));
        comprLvl.setModel(new CustomSpinnerIntModel(0, 0, 7, 1));
        lvlShift.setModel(new CustomSpinnerListModel(Common.LEVEL_SHIFT));
        xtalk.setModel(new CustomSpinnerIntModel(2, 0, 3, 1));
        xtalkGrp.setModel(new CustomSpinnerIntModel(0, 0, 3, 1));
        threshold.setModel(new CustomSpinnerIntModel(20, 0, 127, 1));
        gain.setModel(new CustomSpinnerIntModel(4, 0, 8, 1));
        hiLvlAuto.setModel(new CustomSpinnerListModel(Common.NO_YES));
        hiLvlAuto.setValue(Common.NO_YES[1]);
        highLvl.setModel(new CustomSpinnerIntModel(64, 32, 1023, 1));
        retrigger.setModel(new CustomSpinnerIntModel(8, 1, 127, 1));
        dynLevel.setModel(new CustomSpinnerIntModel(6, 0, 7, 1));
        dynTime.setModel(new CustomSpinnerListModel(Common.DYN_TIME));
        dynTime.setValue(Common.DYN_TIME[2]);
        minScan.setModel(new CustomSpinnerIntModel(20, 1, 100, 1));
        dualHead.setModel(new CustomSpinnerListModel(Common.MM_PP_PS));
        bNote.setModel(new CustomSpinnerNoteModel(0, 0, 127, 1));
        bThreshold.setModel(new CustomSpinnerIntModel(0, 0, 127, 1));

        //set a white background for all fields
        ((JSpinner.DefaultEditor) note.getEditor()).getTextField().setBackground(java.awt.Color.WHITE);
        ((JSpinner.DefaultEditor) note.getEditor()).getTextField().setForeground(java.awt.Color.BLACK);
        ((JSpinner.DefaultEditor) channel.getEditor()).getTextField().setBackground(java.awt.Color.WHITE);
        ((JSpinner.DefaultEditor) channel.getEditor()).getTextField().setForeground(java.awt.Color.BLACK);
        ((JSpinner.DefaultEditor) curve.getEditor()).getTextField().setBackground(java.awt.Color.WHITE);
        ((JSpinner.DefaultEditor) curve.getEditor()).getTextField().setForeground(java.awt.Color.BLACK);
        ((JSpinner.DefaultEditor) comprLvl.getEditor()).getTextField().setBackground(java.awt.Color.WHITE);
        ((JSpinner.DefaultEditor) comprLvl.getEditor()).getTextField().setForeground(java.awt.Color.BLACK);
        ((JSpinner.DefaultEditor) lvlShift.getEditor()).getTextField().setBackground(java.awt.Color.WHITE);
        ((JSpinner.DefaultEditor) lvlShift.getEditor()).getTextField().setForeground(java.awt.Color.BLACK);
        ((JSpinner.DefaultEditor) xtalk.getEditor()).getTextField().setBackground(java.awt.Color.WHITE);
        ((JSpinner.DefaultEditor) xtalk.getEditor()).getTextField().setForeground(java.awt.Color.BLACK);
        ((JSpinner.DefaultEditor) xtalkGrp.getEditor()).getTextField().setBackground(java.awt.Color.WHITE);
        ((JSpinner.DefaultEditor) xtalkGrp.getEditor()).getTextField().setForeground(java.awt.Color.BLACK);
        ((JSpinner.DefaultEditor) threshold.getEditor()).getTextField().setBackground(java.awt.Color.WHITE);
        ((JSpinner.DefaultEditor) threshold.getEditor()).getTextField().setForeground(java.awt.Color.BLACK);
        ((JSpinner.DefaultEditor) gain.getEditor()).getTextField().setBackground(java.awt.Color.WHITE);
        ((JSpinner.DefaultEditor) gain.getEditor()).getTextField().setForeground(java.awt.Color.BLACK);
        ((JSpinner.DefaultEditor) hiLvlAuto.getEditor()).getTextField().setBackground(java.awt.Color.WHITE);
        ((JSpinner.DefaultEditor) hiLvlAuto.getEditor()).getTextField().setForeground(java.awt.Color.BLACK);
        ((JSpinner.DefaultEditor) highLvl.getEditor()).getTextField().setBackground(java.awt.Color.WHITE);
        ((JSpinner.DefaultEditor) highLvl.getEditor()).getTextField().setForeground(java.awt.Color.BLACK);
        ((JSpinner.DefaultEditor) retrigger.getEditor()).getTextField().setBackground(java.awt.Color.WHITE);
        ((JSpinner.DefaultEditor) retrigger.getEditor()).getTextField().setForeground(java.awt.Color.BLACK);
        ((JSpinner.DefaultEditor) dynLevel.getEditor()).getTextField().setBackground(java.awt.Color.WHITE);
        ((JSpinner.DefaultEditor) dynLevel.getEditor()).getTextField().setForeground(java.awt.Color.BLACK);
        ((JSpinner.DefaultEditor) dynTime.getEditor()).getTextField().setBackground(java.awt.Color.WHITE);
        ((JSpinner.DefaultEditor) dynTime.getEditor()).getTextField().setForeground(java.awt.Color.BLACK);
        ((JSpinner.DefaultEditor) minScan.getEditor()).getTextField().setBackground(java.awt.Color.WHITE);
        ((JSpinner.DefaultEditor) minScan.getEditor()).getTextField().setForeground(java.awt.Color.BLACK);
        ((JSpinner.DefaultEditor) dualHead.getEditor()).getTextField().setBackground(java.awt.Color.WHITE);
        ((JSpinner.DefaultEditor) dualHead.getEditor()).getTextField().setForeground(java.awt.Color.BLACK);
        ((JSpinner.DefaultEditor) bNote.getEditor()).getTextField().setBackground(java.awt.Color.WHITE);
        ((JSpinner.DefaultEditor) bNote.getEditor()).getTextField().setForeground(java.awt.Color.BLACK);
        ((JSpinner.DefaultEditor) bThreshold.getEditor()).getTextField().setBackground(java.awt.Color.WHITE);
        ((JSpinner.DefaultEditor) bThreshold.getEditor()).getTextField().setForeground(java.awt.Color.BLACK);

        //add popup slider to fields with high value ranges
        new PopupSlider(note);
        new PopupSlider(threshold);
        new PopupSlider(highLvl);
        new PopupSlider(retrigger);
        new PopupSlider(minScan);
        new PopupSlider(bNote);
        new PopupSlider(bThreshold);
    }

    //first run routine
    private void firstRun() {
        if (firstRun) {
            JOptionPane.showMessageDialog(this,
                    "This seems to be the first time you run \"Megadrum Config Tool\", so WELCOME!\n"
                    + "Now you will be asked to configure the following paramteres:\n"
                    + "____Hardware settings____\n"
                    + "Here you can define which type (hardware) is your Megadrum. It is very important\n"
                    + "to specify the hardware features of your Megadrum for this tool to work properly.\n"
                    + "You mus also set the Chain ID. If you have several Megadrum's connected in chain,\n"
                    + "select the chain ID of the Megadrum you want to configure with this program.\n"
                    + "____MIDI settings____\n"
                    + "Here you must select your Megadrum MIDI interface in a list of MIDI IN & OUT devices.\n"
                    + "For the most of the people it should show in the list as \"Megadrum Midi USB\".\n"
                    + "For some WinXP users it may show with a generic name like \"USB audio device\"\n"
                    + "____Other options____\n"
                    + "There are much more options to configure. Put the mouse over an option to get a\n"
                    + "popup explanation of what it is for.\n",
                    "Information", JOptionPane.INFORMATION_MESSAGE);
            log.info("'First run' configuration wizard executed");
            //show Options panel
            new Options(this);
        }
    }

    //add action listeners for all the menus and buttons and the list
    private void addActionListeners() {

        padList.addListSelectionListener(this);

        menu_exit.addActionListener(this);
        menu_about.addActionListener(this);
        menu_help.addActionListener(this);
        menu_recommendedFirmware.addActionListener(this);

        menu_loadFromFile.addActionListener(this);
        menu_loadFromMega.addActionListener(this);
        menu_saveToFile.addActionListener(this);
        menu_sendToMega.addActionListener(this);
        menu_sendToMegaOrderWrite.addActionListener(this);
        menu_loadPadFromFile.addActionListener(this);
        menu_loadPadFromMega.addActionListener(this);
        menu_savePadToFile.addActionListener(this);
        menu_sendPadToMega.addActionListener(this);
        menu_loadDrumMap.addActionListener(this);
        menu_saveDrumMap.addActionListener(this);
        menu_loadMiscFromFile.addActionListener(this);
        menu_loadMiscFromMega.addActionListener(this);
        menu_saveMiscToFile.addActionListener(this);
        menu_sendMiscToMega.addActionListener(this);
        menu_loadHihatFromFile.addActionListener(this);
        menu_loadHihatFromMega.addActionListener(this);
        menu_saveHihatToFile.addActionListener(this);
        menu_sendHihatToMega.addActionListener(this);
        menu_orderWrite.addActionListener(this);

        menu_drummapEditor.addActionListener(this);
        menu_aftertouchMapping.addActionListener(this);
        menu_customCurves.addActionListener(this);

        menu_updateFirmware.addActionListener(this);
        menu_modifyFirmware.addActionListener(this);
        menu_inputNames.addActionListener(this);
        menu_checkUpdate.addActionListener(this);

        menu_midiLog.addActionListener(this);
        menu_appLog.addActionListener(this);

        menu_options.addActionListener(this);

        b_channel.addActionListener(this);
        b_comprLvl.addActionListener(this);
        b_curve.addActionListener(this);
        b_dynLevel.addActionListener(this);
        b_dynTime.addActionListener(this);
        b_gain.addActionListener(this);
        b_hiLvlAuto.addActionListener(this);
        b_highLvl.addActionListener(this);
        b_loadFromMega1.addActionListener(this);
        b_loadPadFromMega1.addActionListener(this);
        b_loadHihatFromMega1.addActionListener(this);
        b_loadMiscFromMega1.addActionListener(this);
        b_lvlShift.addActionListener(this);
        b_minScan.addActionListener(this);
        b_note.addActionListener(this);
        b_orderWrite.addActionListener(this);
        b_retrigger.addActionListener(this);
        b_sendToMega1.addActionListener(this);
        b_sendToMegaOrderWrite1.addActionListener(this);
        b_sendPadToMega1.addActionListener(this);
        b_sendHihatToMega1.addActionListener(this);
        b_sendMiscToMega1.addActionListener(this);
        b_threshold.addActionListener(this);
        b_xtalk.addActionListener(this);
        b_xtalkGrp.addActionListener(this);
        b_bNote.addActionListener(this);
        b_bThreshold.addActionListener(this);
        b_dualHead.addActionListener(this);
    }

    //add close listener to close the app the right way, without leaving
    //opened midi devices
    private void addCloseListener() {
        this.addWindowListener(new WindowAdapter() {

            @Override
            public void windowClosing(WindowEvent e) {
                closeApp();
            }
        });
    }

    // ####################
    // ### MIDI METHODS ###
    // ####################
    private void initializeMidiDevices() {
        //open input and output
        if (!startInput()) {
            JOptionPane.showMessageDialog(this, "Unable to initialize MIDI IN device.\n"
                    + "Please, go to Options to select an available device.",
                    "Error", JOptionPane.ERROR_MESSAGE);
        }
        if (!startOutput()) {
            JOptionPane.showMessageDialog(this, "Unable to initialize MIDI OUT device.\n"
                    + "Please, go to Options to select an available device.",
                    "Error", JOptionPane.ERROR_MESSAGE);
        }
        if (!startThrough()) {
            JOptionPane.showMessageDialog(this, "Unable to initialize MIDI THROUGH device.\n"
                    + "Please, go to Options to select an available device.",
                    "Error", JOptionPane.ERROR_MESSAGE);
        }
    }

    private void uninitializeMidiDevices() {
        //close input and output
        stopInput();
        stopOutput();
        stopThrough();
    }

    private boolean startInput() {
        try {
            in = MidiCommon.getMidiInputDevice(inputDeviceName);
            if (in == null) {
                return false;
            }
            in.open();
            multiReceiver = new MultiReceiver();
            multiReceiver.add(this); //to receive sysex messages from megadrum and applying changes to parameters
            multiReceiver.add(midiMonitor); //to monitor hihat pedal position and generic pad velocity level
            multiReceiver.add(midiLog); //to monitor pad hits on a separate window
            transmitter = in.getTransmitter();
            transmitter.setReceiver(multiReceiver);
            log.info("<font color=#6cef52>MIDI IN connection opened</font>");
            return true;
        } catch (MidiUnavailableException e) {
            log.error("MIDI IN unavailable");
            return false;
        }
    }

    private void stopInput() {
        this.close();
        midiMonitor.close();
        midiLog.close();
        if (transmitter != null) {
            transmitter.close();
        }
        if (in != null && in.isOpen()) {
            in.close();
            log.info("<font color=#6cef52>MIDI IN connection closed</font>");
        }

    }

    private boolean restartInput() {
        stopInput();
        return startInput();
    }

    private boolean startOutput() {
        try {
            out = MidiCommon.getMidiOutputDevice(outputDeviceName);
            if (out == null) {
                return false;
            }
            out.open();
            log.info("<font color=#6cef52>MIDI OUT connection opened</font>");
            return true;
        } catch (MidiUnavailableException e) {
            log.error("MIDI OUT unavailable");
            return false;
        }
    }

    private void stopOutput() {
        if (out != null && out.isOpen()) {
            out.close();
            log.info("<font color=#6cef52>MIDI OUT connection closed</font>");
        }
    }

    private boolean restartOutput() {
        stopOutput();
        return startOutput();
    }

    private boolean startThrough() {
        //if throughDeviceName is not set, then don't try to initialize it as it is not mandatory
        if (throughDeviceName != null) {
            try {
                through = MidiCommon.getMidiOutputDevice(throughDeviceName);
                if (through == null) {
                    return false;
                }
                through.open();
                throughReceiver = through.getReceiver();
                log.info("<font color=#6cef52>MIDI THROUGH connection opened</font>");
                return true;
            } catch (MidiUnavailableException e) {
                log.error("MIDI THROUGH unavailable");
                return false;
            }
        } else {
            return true;
        }
    }

    private void stopThrough() {
        if (throughReceiver != null) {
            throughReceiver.close();
            throughReceiver = null;
        }
        if (through != null && through.isOpen()) {
            through.close();
            log.info("<font color=#6cef52>MIDI THROUGH connection closed</font>");
        }
    }

    private boolean restartThrough() {
        stopThrough();
        return startThrough();
    }

    //Receiver interface: method for receiving midi messages 
    public void send(MidiMessage event, long time) {
        //NOTE message
        if (event instanceof ShortMessage) {
            //send first of all to the MIDI THROUGH port as we want it to go to other host as fast as possible
            if (throughReceiver != null) {
                MidiCommon.sendMidiEvent((ShortMessage) event, time, throughReceiver);
            }

            switch (event.getStatus() & 0xf0) {
                case 0xb0: //pedal
                    pedalPosition = ((ShortMessage) event).getData2();
                    break;
                case 0x90: //note on
                    if (quickAccessEnabled && pedalPosition > quickAccessPedal
                            && ((ShortMessage) event).getData2() > quickAccessPad) {
                        int n = ((ShortMessage) event).getData1();
                        //jump to pad panel
                        for (int i = 0; i < numberMonoPads; i++) {
                            if (n == pad[i].getValue(Common.MONO, Common.NOTE)) {
                                padList.setSelectedIndex(i);
                                return;
                            }
                        }
                        //stereo inputs
                        for (int i = numberMonoPads; i < pad.length; i++) {
                            if (n == pad[i].getValue(Common.HEAD, Common.NOTE)
                                    || n == pad[i].getValue(Common.RIM, Common.NOTE)
                                    || n == pad[i].getValue(Common.THIRDZONE, Common.BNOTE)) {
                                padList.setSelectedIndex(i);
                                return;
                            }
                        }
                    }
                    break;
            }

            //SYSEX message
        } else if (event instanceof SysexMessage) {
            byte[] sysex = ((SysexMessage) event).getMessage();

            if (sysex.length > 8 && sysex[0] == Common.SYSEX_BEGIN && sysex[1] == Common.SYSEX_ID
                    && sysex[2] == (byte) chainID && sysex[sysex.length - 1] == Common.SYSEX_END) {
                //correct message, see what type it is
                int padN;
                switch (sysex[3]) {
                    case Common.TYPE_MISC:
                        misc.convertSysexToData(sysex);
                        log.info("Miscellaneous config received from MD");
                        break;
                    case Common.TYPE_HIHAT:
                        hihat.convertSysexToData(sysex);
                        log.info("Hihat Pedal config received from MD");
                        break;
                    case Common.TYPE_PAD:
                        padN = (sysex[4] - 1 < numberMonoPads) ? (sysex[4] - 1) : ((sysex[4] + numberMonoPads - 1) / 2); // 0 to 15
                        if (sysex[4] - 1 < numberMonoPads) {
                            pad[padN].convertSysexToData(sysex, Common.MONO);
                        } else {
                            pad[padN].convertSysexToData(sysex, (sysex[4] - 1 - numberMonoPads) % 2);
                        }
                        log.info("Pad config received from MD");
                        break;
                    case Common.TYPE_3ZONE:
                        padN = sysex[4] + numberMonoPads;
                        pad[padN].convertSysexToData(sysex, Common.THIRDZONE);
                        log.info("3rd Zone config received from MD");
                        break;
                    case Common.TYPE_CUSTOMCURVE:
                        customCurves.convertSysexToData(sysex);
                        log.info("Custom curve config received from MD");
                        break;
                    case Common.TYPE_FIRMWAREINFO:
                        int version = Converter.eightBytesToOneInt(sysex[4], sysex[5], sysex[6], sysex[7],
                                sysex[8], sysex[9], sysex[10], sysex[11]);
                        log.info("Firmware Version loaded in MegaDrum is: " + version);
                        //update INI file if version in MD is different from the one stored in the config
                        if (!lastUpdate.equals("" + version)) {
                            lastUpdate = "" + version;
                            updateConfigurationFile();
                        }
                        if (verboseFirmwareInfo) { //show dialog with firmware info
                            JOptionPane.showMessageDialog(this,
                                    "Current firmware version installed is:\n" + version,
                                    "Information", JOptionPane.INFORMATION_MESSAGE);
                        }
                        break;
                }
            }
        }
    }

    //Receiver interface: method for closing receiver
    public void close() {
    }

    // ####################
    // ### DATA METHODS ###
    // ####################
    //get firmware version info to check if there are new firmware updates
    private void getFirmwareInfo(boolean verbose) {
        verboseFirmwareInfo = verbose;
        MidiCommon.sendSysex(SysexRequest.firmwareInfoRequest(chainID), out);
    }

    //prompt the user to update firmware
    private void updateFirmware(byte[] data) {
        //show information message
        boolean executeUpdate = false;
        switch (bootloader) {
            case Common.BOOTLOADER_STANDARD:
                executeUpdate = JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(this,
                        "Boot up your Megadrum with <Left> key pressed.\n"
                        + "Finally, press <Down> when ready to update,\n"
                        + "and select \"Yes\" in this dialog.",
                        "Intructions: Send update now?",
                        JOptionPane.YES_NO_OPTION);
                break;
            case Common.BOOTLOADER_CLOCK_SELECTOR:
            case Common.BOOTLOADER_WITH_SECURITY:
                executeUpdate = JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(this,
                        "Boot up your Megadrum with <Left> key pressed, then:\n"
                        + " - Press <Up> 3 times to set clock to 12Mhz\n"
                        + " - Press <Up> twice to set clock to 24Mhz\n"
                        + " - Press <Up> once to set clock to 20Mhz\n"
                        + " - Don't press <Up> to leave it set to 16Mhz\n"
                        + "Finally, press <Down> when ready to update,\n"
                        + "and select \"Yes\" in this dialog.",
                        "Intructions: Send update now?",
                        JOptionPane.YES_NO_OPTION);
                break;
            case Common.BOOTLOADER_WITH_SECURITY_AND_MIDI:
                MidiFirmwareUpdateDialog mfud = new MidiFirmwareUpdateDialog();
                transmitter.setReceiver(mfud); //convert this dialog in the midi receiver
                //show dialog (message in HTML format)
                executeUpdate =
                        mfud.showDialog(this,
                        "<html>"
                        + "<b>Step 1:</b> Boot up your Megadrum with &lt;Left&gt; key pressed:<br>"
                        + "<b>Step 2:</b><br>"
                        + " - Don't press &lt;Up&gt; to leave it set to 16Mhz<br>"
                        + " - Press &lt;Up&gt; once to set clock to 20Mhz<br>"
                        + " - Press &lt;Up&gt; twice to set clock to 24Mhz<br>"
                        + " - Press &lt;Up&gt; 3 times to set clock to 12Mhz<br>"
                        + "<b>Step 3:</b> Press &lt;Left&gt; to send a confirmation from MD to<br>"
                        + "MCT. If correct clock is selected, the \"Proceed\" button<br>"
                        + "will be enabled.<br>"
                        + "<b>Step 4:</b> Press &lt;Down&gt; when ready to update.<br>"
                        + "<b>Step 5:</b> Click the \"Proceed\" button."
                        + "</html>",
                        "Intructions: Send update now?"); //blocking statement, we'll have to wait for user action
                transmitter.setReceiver(multiReceiver); //restore normal midi receiver
                break;
        }

        if (executeUpdate) {

            //REPLACE INPUT NAMES
            byte[] newdata = replaceInputNames(data);
            if (newdata != null) {
                data = newdata; //replace firmware file
            }            //SEND FIRMWARE AND SHOW RESULTS

            //SEND ERASE SYSEX (if needed) AND FIRMWARE TO MEGADRUM
            sendFirmwareToMD(data);
        }
    }

    private void updateEncryptedFirmware(BinFile fullData) {

        boolean executeUpdate = false;

        MidiFirmwareUpdateDialog mfud = new MidiFirmwareUpdateDialog();
        transmitter.setReceiver(mfud); //convert this dialog in the midi receiver

        //show dialog (message in HTML format)
        executeUpdate =
                mfud.showDialog(this,
                "<html>"
                + "Your MegaDrum should currently be in clock selection mode.<br>"
                + "Next steps:<br>"
                + "<b>Step 1:</b> Press &lt;Left&gt; to confirm update;<br>"
                + "<b>Step 2:</b> Press &lt;Down&gt; to go into live update mode;<br>"
                + "<b>Step 3:</b> Press \"Proceed\" in this window."
                + "</html>",
                "Intructions: Encrypted firmware update");
        transmitter.setReceiver(multiReceiver); //restore normal midi receiver

        if (executeUpdate) {

            mdUpdateData = fullData;

            new Thread() {

                @Override
                public void run() {
                    // In case of an encrypted firmware bootloader
                    V5UpdateDialog myDialog = new V5UpdateDialog();
                    myDialog.setVisible(true);

                    byte[] receivedBuffer;
                    int receivedByte;		// One byte received from COM port
                    int retries = 0;		// Number of tries so far
                    int index;			// Index in the data buffer
                    int frameSize;		// Number of bytes in the current frame

                    int nBytes;
                    int inDelay;

                    int[] data = mdUpdateData.myBuffer;
                    int bufferSize = mdUpdateData.bufferSize;

                    Transmitter MDUpdateTransmitter = null;
                    DumpReceiver MDUpdateDumpReceiver = new DumpReceiver();
                    Receiver MDUpdateReceiver = null;

                    try {
                        MDUpdateTransmitter = in.getTransmitter();
                        MDUpdateTransmitter.setReceiver(MDUpdateDumpReceiver);
                    } catch (MidiUnavailableException e1) {
                        // TODO Auto-generated catch block
                        // e1.printStackTrace();
                    }

                    try {
                        MDUpdateReceiver = out.getReceiver();
                    } catch (MidiUnavailableException e1) {
                        // TODO Auto-generated catch block
                        // e1.printStackTrace();
                    }

                    // Progress bar
                    myDialog.mdUpdateProgress.setMaximum(bufferSize);
                    myDialog.mdUpdateProgress.setValue(0);

                    for (index = 0; index < bufferSize; index += frameSize) {

                        frameSize = ((data[index] << 8) | data[index + 1]) + 2;
                        nBytes = 1;
                        while (nBytes > 0) {
                            receivedBuffer = MDUpdateDumpReceiver.getByteMessage();
                            if (receivedBuffer == null) {
                                nBytes = 0;

                            } else {
                                nBytes = receivedBuffer.length;
                            }
                        }

                        MidiSupport.writeMid(MDUpdateReceiver, data, index, frameSize);

                        nBytes = 0;
                        inDelay = 40;

                        receivedBuffer = null;
                        while ((nBytes == 0) && (inDelay > 0)) {
                            receivedBuffer = MDUpdateDumpReceiver.getByteMessage();
                            if (receivedBuffer != null) {
                                nBytes = receivedBuffer.length;
                            }

                            inDelay--;

                            try {
                                Thread.sleep(50);
                            } catch (InterruptedException e) {
                                // TODO Auto-generated catch block
                                // e.printStackTrace();
                            }
                        }


                        receivedByte = 0;
                        if (nBytes > 2) {
                            receivedByte = receivedBuffer[1] << 4;
                            receivedByte = receivedBuffer[2] | receivedByte;
                        } else {
                            // READ ERROR
                            nBytes = 1;
                            receivedByte = Error_CRC;
                        }

                        if (nBytes > 0) {
                            switch (receivedByte) {
                                case Error_OK:
                                    retries = 0;
                                    break;

                                default: // Error_CRC:
                                    if (++retries < 4) {
                                        index -= frameSize;

                                        // Retry on CRC error
                                        try {
                                            Thread.sleep(10);
                                        } catch (InterruptedException e) {
                                            // TODO Auto-generated catch block
                                            // e.printStackTrace();
                                        }
                                    } else {
                                        // FILE DAMAGED
                                        JOptionPane.showMessageDialog(null,
                                                "Firmware file is damaged.",
                                                "Error!",
                                                JOptionPane.ERROR_MESSAGE);

                                        myDialog.dispose();

                                        updateOnRestart = false;
                                        updateConfigurationFile();
                                        return;
                                    }
                                    break;
                            }
                        } else {
                            JOptionPane.showMessageDialog(null,
                                    "MegaDrum is not responding.",
                                    "Error!",
                                    JOptionPane.ERROR_MESSAGE);

                            myDialog.dispose();

                            updateOnRestart = false;
                            updateConfigurationFile();
                            return;
                        }

                        myDialog.mdUpdateProgress.setValue(index);
                    }

                    myDialog.mdUpdateOk.setEnabled(true);
                    myDialog.setTitle("Update finished");

                    // MegaDrum successfully updated
                    log.info("FIRMWARE UPDATED!");

                    //get the version number of the firmware just written
                    getFirmwareInfo(true);

                    JOptionPane.showMessageDialog(null,
                            "To ensure correct operation please restart MCT now!",
                            "Please restart MCT",
                            JOptionPane.OK_OPTION);

                    updateOnRestart = false;
                    updateConfigurationFile();

                }
            }.start();

        }

    }

    private byte[] replaceInputNames(byte[] data) {
        try {
            //create byte string containing all input names: sInputs
            StringBuffer sb = new StringBuffer();
            byte b2[];
            for (int j = 0; j < numberInputs; j++) {
                b2 = inputName[j].getBytes();
                for (int x = 0; x < b2.length; x++) {
                    sb.append(Integer.toString(0x0000000F & (b2[x] >> 4), 16).toUpperCase());
                    sb.append(Integer.toString(0x0000000F & b2[x], 16).toUpperCase());
                }
                sb.append("00");
            }
            String sinputs = sb.substring(0, sb.length() - 2);

            //HEX data read from a file
            String sdata = new String(data);

            int begin = sdata.indexOf("4B69"); //search for "Ki"
            int line = sdata.substring(0, begin).lastIndexOf(':'); //first IntelHEX line containing the first name "Kick"

            //each line is 43 characters long = 1(:) + 8(header) + 32(data) + 2(checksum)

            //add pre and post bytes to complete a multiple of 32
            int lines = (int) Math.ceil((sinputs.length() - (line + 41 - begin)) / 32.0) + 1;
            sinputs = sdata.substring(line + 9, begin) + sinputs;
            int end = sinputs.length() % 32;
            sinputs = sinputs + sdata.substring(line + 45 * (lines - 1) + 9 + end, line + 45 * (lines - 1) + 41);
            String cb = null;
            String newsdata = sdata.substring(0, line);
            for (int i = 0; i < lines; i++) {
                cb = Converter.checksum(sdata.substring(line + 45 * i + 1, line + 45 * i + 9) + sinputs.substring(i * 32, i * 32 + 32));
                newsdata = newsdata + sdata.substring(line + 45 * i, line + 45 * i + 9)
                        + sinputs.substring(i * 32, i * 32 + 32)
                        + cb
                        + sdata.substring(line + 45 * i + 43, line + 45 * (i + 1));
            }
            newsdata = newsdata + sdata.substring(line + 45 * lines);

            return newsdata.getBytes();
        } catch (Exception e) {
            log.error("Trying to replace input names in firmware<br>The error was: " + Log.getExceptionInfo(e));
            return null;
        }
    }

    private void loadSession() {
        byte b[] = SysexFile.loadFile(Common.getUserProfileFolder() + Common.DEFAULT_CONFIG_FILE);
        if (b != null && b.length > 0) {
            if (!applyAllSettingsToGUI(b)) {
                log.warn("Unable to reload Megadrum settings from last session");
                JOptionPane.showMessageDialog(this, "Unable to reload settings from previous session.",
                        "Warning", JOptionPane.WARNING_MESSAGE);
            } else {
                log.info("Megadrum settings from last session read successfully");
            }
        } else {
            log.warn("Could not read file containing Megadrum settings from last session:<br>"
                    + Common.getUserProfileFolder() + Common.DEFAULT_CONFIG_FILE);
        }
    }

    public byte[] getAllSettingsFromGUI() {
        try {
            byte[] b = new byte[(Common.LENGTH_HIHAT
                    + Common.LENGTH_MISC
                    + numberMonoPads * Common.LENGTH_PAD
                    + numberStereoPads * (Common.LENGTH_PAD * 2 + Common.LENGTH_3ZONE))];
            int i = 0;
            b = Converter.insertBytes(b, misc.convertDataToSysex(), i);
            i += Common.LENGTH_MISC;
            b = Converter.insertBytes(b, hihat.convertDataToSysex(), i);
            i += Common.LENGTH_HIHAT;
            for (int j = 0; j < numberMonoPads; j++) {
                b = Converter.insertBytes(b, pad[j].convertDataToSysex(Common.MONO), i);
                i += Common.LENGTH_PAD;
            }
            for (int j = numberMonoPads; j < pad.length; j++) {
                b = Converter.insertBytes(b, pad[j].convertDataToSysex(Common.HEAD), i);
                i += Common.LENGTH_PAD;
                b = Converter.insertBytes(b, pad[j].convertDataToSysex(Common.RIM), i);
                i += Common.LENGTH_PAD;
                b = Converter.insertBytes(b, pad[j].convertDataToSysex(Common.THIRDZONE), i);
                i += Common.LENGTH_3ZONE;
            }
            return b;
        } catch (Exception e) {
            log.error("Trying to convert current settings to sysex<br>The error was: " + Log.getExceptionInfo(e));
            return null;
        }
    }

    public boolean applyAllSettingsToGUI(byte sysex[]) {
        try {
            if (sysex.length == (Common.LENGTH_HIHAT + //WE CHECK ONLY SOME CONDITIONS TO TRUST THIS FILE
                    Common.LENGTH_MISC
                    + numberMonoPads * Common.LENGTH_PAD
                    + numberStereoPads * (Common.LENGTH_PAD * 2 + Common.LENGTH_3ZONE))
                    && sysex[0] == Common.SYSEX_BEGIN
                    && sysex[1] == Common.SYSEX_ID
                    && sysex[3] == Common.TYPE_MISC
                    && sysex[Common.LENGTH_MISC + 3] == Common.TYPE_HIHAT
                    && sysex[sysex.length - 1] == Common.SYSEX_END) {
                //the file seems to be ok => apply settings
                int i = 0;
                misc.convertSysexToData(Converter.subByteArray(sysex, i, i + Common.LENGTH_MISC));
                i += Common.LENGTH_MISC;
                hihat.convertSysexToData(Converter.subByteArray(sysex, i, i + Common.LENGTH_HIHAT));
                i += Common.LENGTH_HIHAT;
                for (int j = 0; j < numberMonoPads; j++) {
                    pad[j].convertSysexToData(Converter.subByteArray(sysex, i, i + Common.LENGTH_PAD), Common.MONO);
                    i += Common.LENGTH_PAD;
                }
                for (int j = numberMonoPads; j < pad.length; j++) {
                    pad[j].convertSysexToData(Converter.subByteArray(sysex, i, i + Common.LENGTH_PAD), Common.HEAD);
                    i += Common.LENGTH_PAD;
                    pad[j].convertSysexToData(Converter.subByteArray(sysex, i, i + Common.LENGTH_PAD), Common.RIM);
                    i += Common.LENGTH_PAD;
                    pad[j].convertSysexToData(Converter.subByteArray(sysex, i, i + Common.LENGTH_3ZONE), Common.THIRDZONE);
                    i += Common.LENGTH_3ZONE;
                }
                return true;
            } else {
                return false;
            }
        } catch (Exception e) {
            log.error("Trying to apply all settings from sysex<br>The error was: " + Log.getExceptionInfo(e));
            return false;
        }
    }

    public byte[] getMiscSettingsFromGUI() {
        try {
            return misc.convertDataToSysex();
        } catch (Exception e) {
            log.error("Trying to convert current settings to sysex<br>The error was: " + Log.getExceptionInfo(e));
            return null;
        }
    }

    public boolean applyMiscSettingsToGUI(byte sysex[]) {
        try {
            if (sysex.length == Common.LENGTH_MISC
                    && sysex[0] == Common.SYSEX_BEGIN && //WE CHECK ONLY SOME CONDITIONS TO TRUST THIS FILE
                    sysex[1] == Common.SYSEX_ID
                    && sysex[3] == Common.TYPE_MISC
                    && sysex[Common.LENGTH_MISC - 1] == Common.SYSEX_END) {
                misc.convertSysexToData(sysex);
                return true;
            } else {
                return false;
            }
        } catch (Exception e) {
            log.error("Trying to apply misc settings from sysex<br>The error was: " + Log.getExceptionInfo(e));
            return false;
        }
    }

    public byte[] getHihatSettingsFromGUI() {
        try {
            return hihat.convertDataToSysex();
        } catch (Exception e) {
            log.error("Trying to convert current settings to sysex<br>The error was: " + Log.getExceptionInfo(e));
            return null;
        }
    }

    public boolean applyHihatSettingsToGUI(byte sysex[]) {
        try {
            if (sysex.length == Common.LENGTH_HIHAT
                    && sysex[0] == Common.SYSEX_BEGIN && //WE CHECK ONLY SOME CONDITIONS TO TRUST THIS FILE
                    sysex[1] == Common.SYSEX_ID
                    && sysex[3] == Common.TYPE_HIHAT
                    && sysex[Common.LENGTH_HIHAT - 1] == Common.SYSEX_END) {
                hihat.convertSysexToData(sysex);
                return true;
            } else {
                return false;
            }
        } catch (Exception e) {
            log.error("Trying to apply hihat settings from sysex<br>The error was: " + Log.getExceptionInfo(e));
            return false;
        }
    }

    public byte[] getPadSettingsFromGUI() {
        try {
            byte[] b;
            if (numberMonoPads > padList.getSelectedIndex()) { //a mono pad has been selected => send 1 request: mono(=rim)
                b = pad[padList.getSelectedIndex()].convertDataToSysex(Common.MONO);
            } else { // an stereo pad has been selected => send 3 request: head, rim and 3rd zone
                b = new byte[Common.LENGTH_PAD * 2 + Common.LENGTH_3ZONE];
                b = Converter.insertBytes(b, pad[padList.getSelectedIndex()].convertDataToSysex(Common.HEAD), 0);
                b = Converter.insertBytes(b, pad[padList.getSelectedIndex()].convertDataToSysex(Common.RIM), Common.LENGTH_PAD);
                b = Converter.insertBytes(b, pad[padList.getSelectedIndex()].convertDataToSysex(Common.THIRDZONE), Common.LENGTH_PAD * 2);
            }
            return b;
        } catch (Exception e) {
            log.error("Trying to convert current settings to sysex<br>The error was: " + Log.getExceptionInfo(e));
            return null;
        }
    }

    public boolean applyPadSettingsToGUI(byte sysex[]) {
        try {
            if (numberMonoPads > padList.getSelectedIndex() && //a mono pad has been selected => load mono(=rim)
                    sysex.length == Common.LENGTH_PAD
                    && sysex[0] == Common.SYSEX_BEGIN
                    && sysex[1] == Common.SYSEX_ID
                    && sysex[3] == Common.TYPE_PAD
                    && sysex[Common.LENGTH_PAD - 1] == Common.SYSEX_END) {

                pad[padList.getSelectedIndex()].convertSysexToData(sysex, Common.MONO);
                return true;

            } else if (numberMonoPads <= padList.getSelectedIndex() && // an stereo pad has been selected => load head, rim and 3rd zone
                    sysex.length == Common.LENGTH_PAD * 2 + Common.LENGTH_3ZONE
                    && sysex[0] == Common.SYSEX_BEGIN
                    && sysex[1] == Common.SYSEX_ID
                    && sysex[3] == Common.TYPE_PAD
                    && sysex[Common.LENGTH_PAD + 3] == Common.TYPE_PAD
                    && sysex[Common.LENGTH_PAD * 2 + 3] == Common.TYPE_3ZONE
                    && sysex[Common.LENGTH_PAD * 2 + Common.LENGTH_3ZONE - 1] == Common.SYSEX_END) {

                pad[padList.getSelectedIndex()].convertSysexToData(Converter.subByteArray(sysex, 0, Common.LENGTH_PAD), Common.HEAD);
                pad[padList.getSelectedIndex()].convertSysexToData(Converter.subByteArray(sysex, Common.LENGTH_PAD, Common.LENGTH_PAD * 2), Common.RIM);
                pad[padList.getSelectedIndex()].convertSysexToData(Converter.subByteArray(sysex, Common.LENGTH_PAD * 2, Common.LENGTH_3ZONE), Common.THIRDZONE);
                return true;

            } else {
                return false;
            }
        } catch (Exception e) {
            log.error("Trying to apply pad settings from sysex<br>The error was: " + Log.getExceptionInfo(e));
            return false;
        }
    }

    private int saveDrumMap() {
        FileOutputStream outS = null;
        String path = null;
        try {
            path = SysexFile.getPath(this, "Save Drum Map file", "mdm", false);
            if (path == null) {
                return Common.CANCEL;
            }
            Properties p = new Properties();
            //hihat
            p.setProperty("CCValue", "" + hihat.getValue(Common.CCVALUE));
            p.setProperty("BowHO", "" + hihat.getValue(Common.BOWHO));
            p.setProperty("EdgeHO", "" + hihat.getValue(Common.EDGEHO));
            p.setProperty("BowCL", "" + hihat.getValue(Common.BOWCL));
            p.setProperty("EdgeCL", "" + hihat.getValue(Common.EDGECL));
            p.setProperty("Chick", "" + hihat.getValue(Common.CHICKNOTE));
            p.setProperty("Splash", "" + hihat.getValue(Common.SPLASHNOTE));
            //mono inputs
            for (int i = 0; i < numberMonoPads; i++) {
                p.setProperty("Input" + (i + 1), "" + pad[i].getValue(Common.MONO, Common.NOTE));
                p.setProperty("Channel" + (i + 1), "" + pad[i].getValue(Common.MONO, Common.CHANNEL));
            }
            //stereo inputs
            for (int i = numberMonoPads; i < pad.length; i++) {
                p.setProperty("Input" + (i * 2 - numberMonoPads + 1), "" + pad[i].getValue(Common.HEAD, Common.NOTE)); //head
                p.setProperty("Channel" + (i * 2 - numberMonoPads + 1), "" + pad[i].getValue(Common.HEAD, Common.CHANNEL));
                p.setProperty("Input" + (i * 2 - numberMonoPads + 2), "" + pad[i].getValue(Common.RIM, Common.NOTE)); //rim
                p.setProperty("Channel" + (i * 2 - numberMonoPads + 2), "" + pad[i].getValue(Common.RIM, Common.CHANNEL));
                p.setProperty("3rdZone" + (i * 2 - numberMonoPads + 2), "" + pad[i].getValue(Common.THIRDZONE, Common.BNOTE)); //3rd zone
            }
            outS = new FileOutputStream(path);
            p.store(outS, "Megadrum Drum Map");
            outS.close();
            log.info("Drummap saved from<br>" + path);
            return Common.OK;
        } catch (Exception e) {
            try {
                outS.close();
            } catch (Exception e2) {
            }
            log.error("Drummap not saved from<br>" + path + "<br>The error was: " + Log.getExceptionInfo(e));
            return Common.ERROR;
        }
    }

    private int loadDrumMap() {
        FileInputStream inS = null;
        String path = null;
        try {
            path = SysexFile.getPath(this, "Load Drum Map file", "mdm", true);
            if (path == null) {
                return Common.CANCEL;
            }
            Properties p = new Properties();
            inS = new FileInputStream(path);
            p.load(inS);
            //hihat
            hihat.setValue(Common.CCVALUE, Integer.parseInt(p.getProperty("CCValue")));
            hihat.setValue(Common.BOWHO, Integer.parseInt(p.getProperty("BowHO")));
            hihat.setValue(Common.EDGEHO, Integer.parseInt(p.getProperty("EdgeHO")));
            hihat.setValue(Common.BOWCL, Integer.parseInt(p.getProperty("BowCL")));
            hihat.setValue(Common.EDGECL, Integer.parseInt(p.getProperty("EdgeCL")));
            hihat.setValue(Common.CHICKNOTE, Integer.parseInt(p.getProperty("Chick")));
            hihat.setValue(Common.SPLASHNOTE, Integer.parseInt(p.getProperty("Splash")));
            //mono inputs
            for (int i = 0; i < numberMonoPads; i++) {
                pad[i].setValue(Common.MONO, Common.NOTE, Integer.parseInt(p.getProperty("Input" + (i + 1))));
                pad[i].setValue(Common.MONO, Common.CHANNEL, Integer.parseInt(p.getProperty("Channel" + (i + 1))));
            }
            //stereo inputs
            for (int i = numberMonoPads; i < pad.length; i++) {
                pad[i].setValue(Common.HEAD, Common.NOTE, Integer.parseInt(p.getProperty("Input" + (i * 2 - numberMonoPads + 1)))); //head
                pad[i].setValue(Common.HEAD, Common.CHANNEL, Integer.parseInt(p.getProperty("Channel" + (i * 2 - numberMonoPads + 1))));
                pad[i].setValue(Common.RIM, Common.NOTE, Integer.parseInt(p.getProperty("Input" + (i * 2 - numberMonoPads + 2)))); //rim
                pad[i].setValue(Common.RIM, Common.CHANNEL, Integer.parseInt(p.getProperty("Channel" + (i * 2 - numberMonoPads + 2))));
                pad[i].setValue(Common.THIRDZONE, Common.NOTE, Integer.parseInt(p.getProperty("3rdZone" + (i * 2 - numberMonoPads + 2)))); //3rd zone
            }
            inS.close();
            log.info("Drummap loaded from<br>" + path);
            return Common.OK;
        } catch (Exception e) {
            try {
                inS.close();
            } catch (Exception e2) {
            }
            log.error("Drummap not loaded from<br>" + path + "<br>The error was: " + Log.getExceptionInfo(e));
            return Common.ERROR;
        }
    }

    private void installMidiPort() {

        String path = null;
        try {
            path = System.getProperty("sun.boot.library.path"); //get JRE library path to install there the MidiPort.dll
            for (int i = 0; i < path.length() - 1; i++) {
                if (path.charAt(i) == '\\') {
                    path = path.substring(0, i) + "/" + path.substring(i + 1);
                }
            }
            if (path.charAt(path.length() - 1) == '\\') {
                path = path.substring(0, path.length() - 1);
            }
            path = path + "/";

            File f = new File(path + "MidiPort.dll");
            if (!f.exists()) {
                byte buffer[] = new byte[Common.MIDIPORT_FILE_SIZE];
                BufferedInputStream input =
                        new BufferedInputStream(getClass().getClassLoader().getResourceAsStream("jmidi/MidiPort.dll"));
                int read = input.read(buffer, 0, buffer.length);
                if (read != Common.MIDIPORT_FILE_SIZE) {
                    throw new java.io.IOException("Unable to read completely the DLL file");
                }
                input.close();
                BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(path + "MidiPort.dll"));
                bos.write(buffer, 0, buffer.length);
                bos.close();
                log.info("MidiPort.dll library installed to:<br>" + path);
                JOptionPane.showMessageDialog(this,
                        "In order to be able to send firmware updates to Megadrum,\n"
                        + "this program has installed the MidiPort DLL file to:\n" + path, "Information", JOptionPane.INFORMATION_MESSAGE);
            }
            return;
        } catch (Exception e) {
            log.error("Trying to install MidiPort DLL Library to:<br>" + path + "<br>The error was: " + Log.getExceptionInfo(e));
            JOptionPane.showMessageDialog(this, "Error trying to install MidiPort DLL Library to:\n" + path,
                    "Error", JOptionPane.ERROR_MESSAGE);
        }
    }

    // ####################
    // ### TASK METHODS ###
    // ####################
    //  ACTIONS FOR ALL SETTINGS
    public void loadAllSettingsFromMD() {
        if (!processingTask) {
            progressBarTimer.stop();
            processingTask = true;
            progressBar.setIndeterminate(true);
            progressBar.setString("Loading all settings from MD");
            new Thread() {

                @Override
                public void run() {
                    byte[][] sysex = new byte[numberMonoPads + numberStereoPads * 3 + 2][0];
                    int j = -1;
                    //request misc
                    sysex[++j] = SysexRequest.miscConfigRequest(chainID);
                    //request hihat
                    sysex[++j] = SysexRequest.hihatConfigRequest(chainID);
                    //request firstly the mono inputs
                    for (int i = 0; i < numberMonoPads; i++) {
                        sysex[++j] = SysexRequest.subpadConfigRequest(chainID, i + 1); //Mono
                    }
                    //second, request stereo inputs
                    for (int i = numberMonoPads; i < pad.length; i++) {
                        sysex[++j] = SysexRequest.subpadConfigRequest(chainID, i * 2 - numberMonoPads + 1); //head
                        sysex[++j] = SysexRequest.subpadConfigRequest(chainID, i * 2 - numberMonoPads + 2); //rim
                        sysex[++j] = SysexRequest.thirdZoneConfigRequest(chainID, i - numberMonoPads); //3rd zone
                    }

                    MidiCommon.sendMultipleSysex(sysex, out, sysexWaitTime);

                    progressBar.setString("All settings loaded");
                    progressBar.setIndeterminate(false);
                    processingTask = false;
                    progressBarTimer.restart(); //auto-remove message from progress bar after 5 seconds
                }
            }.start();
        }
    }

    public void sendAllSettingsToMD() {
        if (!processingTask) {
            progressBarTimer.stop();
            processingTask = true;
            progressBar.setIndeterminate(true);
            progressBar.setString("Sending all settings to MD");
            new Thread() {

                @Override
                public void run() {
                    //number of messages = monoPads (Kick) + stereo pads (head,rim,3rdZone) + misc + hihat
                    int numberOfMessages = numberMonoPads + numberStereoPads * 3 + 2;
                    byte[][] sysex = new byte[numberOfMessages][0];
                    int j = -1;
                    //send misc
                    sysex[++j] = misc.convertDataToSysex();
                    //send hihat
                    sysex[++j] = hihat.convertDataToSysex();
                    //send the mono inputs
                    for (int i = 0; i < Common.NUMBER_OF_MONO_PADS; i++) {
                        sysex[++j] = pad[i].convertDataToSysex(Common.MONO); //Mono
                    }
                    //send stereo inputs
                    for (int i = numberMonoPads; i < pad.length; i++) {
                        sysex[++j] = pad[i].convertDataToSysex(Common.HEAD); //head
                        sysex[++j] = pad[i].convertDataToSysex(Common.RIM); //rim
                        sysex[++j] = pad[i].convertDataToSysex(Common.THIRDZONE); //3rd zone
                    }

                    if (MidiCommon.sendMultipleSysex(sysex, out, sysexWaitTime)) {
                        log.info("All settings sent to MD and saved into Slot 1");
                    }
                    progressBar.setString("All settings sent");
                    progressBar.setIndeterminate(false);
                    processingTask = false;
                    progressBarTimer.restart(); //auto-remove message from progress bar after 5 seconds

                    //if closing action is waiting for settings to be sent, notify that we are finished
                    if (closingApp) {
                        synchronized (closeMonitor) {
                            closeMonitor.notify();
                        }
                    }
                }
            }.start();
        }
    }

    public void sendAllSettingsToMDAndWriteToSlot1() {
        if (!processingTask) {
            progressBarTimer.stop();
            processingTask = true;
            progressBar.setIndeterminate(true);
            progressBar.setString("Sending all settings to MD");
            new Thread() {

                @Override
                public void run() {
                    //number of messages = monoPads (Kick) + stereo pads (head,rim,3rdZone) + (misc + hihat + writeToSlot1)
                    int numberOfMessages = numberMonoPads + numberStereoPads * 3 + 3;
                    byte[][] sysex = new byte[numberOfMessages][0];
                    int j = -1;
                    //send misc
                    sysex[++j] = misc.convertDataToSysex();
                    //send hihat
                    sysex[++j] = hihat.convertDataToSysex();
                    //send the mono inputs
                    for (int i = 0; i < Common.NUMBER_OF_MONO_PADS; i++) {
                        sysex[++j] = pad[i].convertDataToSysex(Common.MONO); //Mono
                    }
                    //send stereo inputs
                    for (int i = numberMonoPads; i < pad.length; i++) {
                        sysex[++j] = pad[i].convertDataToSysex(Common.HEAD); //head
                        sysex[++j] = pad[i].convertDataToSysex(Common.RIM); //rim
                        sysex[++j] = pad[i].convertDataToSysex(Common.THIRDZONE); //3rd zone
                    }
                    //write to slot 1
                    sysex[++j] = SysexRequest.writeEEPROMRequest(chainID);

                    if (MidiCommon.sendMultipleSysex(sysex, out, sysexWaitTime)) {
                        log.info("All settings sent to MD and saved into Slot 1");
                    }
                    progressBar.setString("All settings sent and saved in Slot 1");
                    progressBar.setIndeterminate(false);
                    processingTask = false;
                    progressBarTimer.restart(); //auto-remove message from progress bar after 5 seconds

                    //if closing action is waiting for settings to be sent, notify that we are finished
                    if (closingApp) {
                        synchronized (closeMonitor) {
                            closeMonitor.notify();
                        }
                    }
                }
            }.start();
        }
    }

    //  ACTIONS FOR MISC SETTINGS
    public void loadMiscSettingsFromMD() {
        if (!processingTask) {
            progressBarTimer.stop();
            processingTask = true;
            progressBar.setIndeterminate(true);
            progressBar.setString("Loading mis settings from MD");
            new Thread() {

                @Override
                public void run() {
                    MidiCommon.sendSysex(SysexRequest.miscConfigRequest(chainID), out);
                    progressBar.setString("Misc settings loaded");
                    progressBar.setIndeterminate(false);
                    processingTask = false;
                    progressBarTimer.restart(); //auto-remove message from progress bar after 5 seconds
                }
            }.start();
        }
    }

    public void sendMiscSettingsToMD() {
        if (!processingTask) {
            progressBarTimer.stop();
            processingTask = true;
            progressBar.setIndeterminate(true);
            progressBar.setString("Sending misc settings to MD");
            new Thread() {

                @Override
                public void run() {
                    if (MidiCommon.sendSysex(misc.convertDataToSysex(), out)) {
                        log.info("Miscellaneous settings sent to MD");
                    }
                    progressBar.setString("Misc settings sent");
                    progressBar.setIndeterminate(false);
                    processingTask = false;
                    progressBarTimer.restart(); //auto-remove message from progress bar after 5 seconds
                }
            }.start();
        }
    }

    //  ACTIONS FOR HIHAT SETTINGS
    public void loadHihatSettingsFromMD() {
        if (!processingTask) {
            progressBarTimer.stop();
            processingTask = true;
            progressBar.setIndeterminate(true);
            progressBar.setString("Loading hihat settings from MD");
            new Thread() {

                @Override
                public void run() {
                    MidiCommon.sendSysex(SysexRequest.hihatConfigRequest(chainID), out);
                    progressBar.setString("Hihat settings loaded");
                    progressBar.setIndeterminate(false);
                    processingTask = false;
                    progressBarTimer.restart(); //auto-remove message from progress bar after 5 seconds
                }
            }.start();
        }
    }

    public void sendHihatSettingsToMD() {
        if (!processingTask) {
            progressBarTimer.stop();
            processingTask = true;
            progressBar.setIndeterminate(true);
            progressBar.setString("Sending hihat settings to MD");
            new Thread() {

                @Override
                public void run() {
                    if (MidiCommon.sendSysex(hihat.convertDataToSysex(), out)) {
                        log.info("Hihat Pedal settings sent to MD");
                    }
                    progressBar.setString("Hihat settings sent");
                    progressBar.setIndeterminate(false);
                    processingTask = false;
                    progressBarTimer.restart(); //auto-remove message from progress bar after 5 seconds
                }
            }.start();
        }
    }

    //  ACTIONS FOR PAD SETTINGS
    private void loadPadSettingsFromMD() {
        if (!processingTask) {
            progressBarTimer.stop();
            processingTask = true;
            progressBar.setIndeterminate(true);
            progressBar.setString("Loading pad settings from MD");
            new Thread() {

                @Override
                public void run() {
                    if (numberMonoPads > padList.getSelectedIndex()) { //a mono pad has been selected => send 1 request: mono(=rim)
                        MidiCommon.sendSysex(SysexRequest.subpadConfigRequest(chainID, pad[padList.getSelectedIndex()].getInputNumber()), out);
                    } else { // an stereo pad has been selected => send 3 request: head, rim and 3rd zone
                        byte[][] sysex = new byte[3][0];
                        sysex[0] = SysexRequest.subpadConfigRequest(chainID, pad[padList.getSelectedIndex()].getInputNumber());
                        sysex[1] = SysexRequest.subpadConfigRequest(chainID, pad[padList.getSelectedIndex()].getInputNumber() + 1);
                        sysex[2] = SysexRequest.thirdZoneConfigRequest(chainID, padList.getSelectedIndex() - numberMonoPads);
                        MidiCommon.sendMultipleSysex(sysex, out, sysexWaitTime);
                    }
                    progressBar.setString("Pad settings loaded");
                    progressBar.setIndeterminate(false);
                    processingTask = false;
                    progressBarTimer.restart(); //auto-remove message from progress bar after 5 seconds
                }
            }.start();
        }
    }

    public void sendPadSettingsToMD() {
        if (!processingTask) {
            progressBarTimer.stop();
            processingTask = true;
            progressBar.setIndeterminate(true);
            progressBar.setString("Sending pad settings to MD");
            new Thread() {

                @Override
                public void run() {
                    boolean ok;
                    if (numberMonoPads > currentPadNumber) { //a mono pad has been selected => send 1 request: mono(=rim)
                        ok = MidiCommon.sendSysex(pad[currentPadNumber].convertDataToSysex(Common.MONO), out);
                    } else { // an stereo pad has been selected => send 3 request: head, rim and 3rd zone
                        byte[][] sysex = new byte[3][0];
                        sysex[0] = pad[currentPadNumber].convertDataToSysex(Common.HEAD);
                        sysex[1] = pad[currentPadNumber].convertDataToSysex(Common.RIM);
                        sysex[2] = pad[currentPadNumber].convertDataToSysex(Common.THIRDZONE);
                        ok = MidiCommon.sendMultipleSysex(sysex, out, sysexWaitTime);
                    }
                    if (ok) {
                        log.info("Pad settings sent to MD");
                    }
                    progressBar.setString("Pad settings sent");
                    progressBar.setIndeterminate(false);
                    processingTask = false;
                    progressBarTimer.restart(); //auto-remove message from progress bar after 5 seconds
                }
            }.start();
        }
    }

    //  ACTIONS FOR CUSTOM CURVES
    public void loadCustomCurveFromMD(int curve) {
        if (!processingTask) {
            tempInt = curve;
            progressBarTimer.stop();
            processingTask = true;
            progressBar.setIndeterminate(true);
            progressBar.setString("Loading custom curve from MD");
            new Thread() {

                @Override
                public void run() {
                    MidiCommon.sendSysex(SysexRequest.customCurveRequest(chainID, tempInt), out);
                    progressBar.setString("Custom curve loaded");
                    progressBar.setIndeterminate(false);
                    processingTask = false;
                    progressBarTimer.restart(); //auto-remove message from progress bar after 5 seconds
                }
            }.start();
        }
    }

    public void sendCustomCurveToMD(byte[] data) {
        if (!processingTask) {
            tempData = data;
            progressBarTimer.stop();
            processingTask = true;
            progressBar.setIndeterminate(true);
            progressBar.setString("Sending custom curve to MD");

            new Thread() {

                @Override
                public void run() {

                    if (MidiCommon.sendSysex(tempData, out)) {
                        log.info("Custom curve sent to MD");
                    }
                    progressBar.setString("Custom curve sent");
                    progressBar.setIndeterminate(false);
                    processingTask = false;
                    progressBarTimer.restart(); //auto-remove message from progress bar after 5 seconds
                }
            }.start();
        }
    }

    //  FIRMWARE
    public void sendFirmwareToMD(byte[] data) {
        if (!processingTask) {
            tempData = data;
            progressBarTimer.stop();
            processingTask = true;
            progressBar.setIndeterminate(true);
            progressBar.setString("Sending firmware to MD");
            new Thread() {

                @Override
                public void run() {
                    boolean ok;
                    //IF SECURITY SYSEX NEEDED, SEND IT NOW
                    if (Common.getOS() == Common.WINDOWS) {
                        //erase sysex
                        if (bootloader >= Common.BOOTLOADER_WITH_SECURITY) {
                            ok = MidiCommon.sendSysexThroughMidiPort(
                                    Converter.concatenateByteArrays(
                                    SysexRequest.firmwareUpdateRequest(),
                                    tempData),
                                    outputDeviceName); //special sendSysex call for windows users
                        } else {
                            ok = MidiCommon.sendSysexThroughMidiPort(tempData, outputDeviceName); //special sendSysex call for windows users
                        }
                    } else {
                        //erase sysex
                        if (bootloader >= Common.BOOTLOADER_WITH_SECURITY) {
                            ok = MidiCommon.sendSysex(
                                    Converter.concatenateByteArrays(
                                    SysexRequest.firmwareUpdateRequest(),
                                    tempData),
                                    out); //special sendSysex call for windows users
                        } else {
                            ok = MidiCommon.sendSysex(tempData, out); //special sendSysex call for windows users
                        }
                    }
                    if (ok) {
                        log.info("FIRMWARE UPDATED!");
                        tempData = null;
                        progressBar.setString("Firmware sent");
                        progressBar.setIndeterminate(false);
                        processingTask = false;
                        progressBarTimer.restart(); //auto-remove message from progress bar after 5 seconds
                        JOptionPane.showMessageDialog(parent,
                                "The firmware has been sent.\n"
                                + "Wait a few seconds for the firmware to be written.\n"
                                + "Megadrum should automatically restart after the update.\n"
                                + "If not, restart it manually.\n"
                                + "----------------------------\n"
                                + "Remember that if you find strange behavior after the update,\n"
                                + "you can reset Megadrum to default config by rebooting it\n"
                                + "with the <Right> key pressed for a few seconds.\n"
                                + "----------------------------\n"
                                + "After pressing the \"Accept\" button, MCT will check\n"
                                + "which version is now installed in your Megadrum.",
                                "Intructions", JOptionPane.INFORMATION_MESSAGE);

                        //get the version number of the firmware just written
                        getFirmwareInfo(true);

                    } else {
                        log.error("Updating firmware");
                        tempData = null;
                        progressBar.setString("Error sending firmware");
                        progressBar.setIndeterminate(false);
                        processingTask = false;
                        progressBarTimer.restart(); //auto-remove message from progress bar after 5 seconds
                        JOptionPane.showMessageDialog(parent,
                                "Error trying to send the sysex file", "Error", JOptionPane.ERROR_MESSAGE);
                    }
                }
            }.start();
        }
    }

    public void checkForFirmwareUpdates(boolean verbose) {
        if (!processingTask) {
            verboseCheckUpdate = verbose;
            progressBarTimer.stop();
            processingTask = true;
            progressBar.setIndeterminate(true);
            progressBar.setString("Checking for firmware updates");
            new Thread() {

                @Override
                public void run() {
                    if (!verboseCheckUpdate) { //if called from the background...
                        //wait 1 second for the firmware info to be read and then check for updates
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                        }
                    }
                    try {
                        URL url = new URL(Common.FIRMWARE_FOLDER_URL + Common.LATEST_VERSION_FILE);
                        BufferedReader inR = new BufferedReader(new InputStreamReader(url.openStream()));
                        String latest = inR.readLine();
                        inR.close();

                        //if latest version is higher than current, download it and ask for update
                        if (latest != null && latest.compareTo(lastUpdate) > 0) {
                            log.info("NEW Megadrum's firmware has been released: " + latest);
                            String firmwareName = "megadrum" + MCU + "_" + clock + "_";
                            if (numberInputs != 21) {
                                firmwareName = firmwareName + (numberInputs + 1) + "_";
                            }
                            firmwareName = firmwareName + latest + ".syx";

                            progressBar.setString("Downloading new firmware: " + latest);

                            url = new URL(Common.FIRMWARE_FOLDER_URL + firmwareName);
                            java.net.URLConnection urlconnection = url.openConnection();
                            int length = urlconnection.getContentLength();

                            byte buffer[] = new byte[length];
                            InputStream input = url.openStream();

                            int read = 0, offset = 0;
                            while (read != -1) {
                                read = input.read(buffer, offset, buffer.length - offset);
                                offset = offset + read;
                            }
                            input.close();

                            //If the version number has been detected, show to the user
                            String message = "";
                            if (!lastUpdate.equals(Common.DEFAULT_LASTUPDATE)) {
                                message = "Your firmware version is " + lastUpdate + ".\n";
                            }
                            progressBar.setString("A new firmware is ready!");
                            progressBar.setIndeterminate(false);
                            processingTask = false;
                            progressBarTimer.restart();

                            int r = JOptionPane.showConfirmDialog(parent,
                                    message
                                    + "A new version has been released (" + latest + ").\n"
                                    + "Do you want to update your Megadrum now?",
                                    "New firmware version released!!", JOptionPane.YES_NO_OPTION);
                            //user selected Yes, download it and call the update method
                            if (r == JOptionPane.YES_OPTION) {
                                updateFirmware(buffer);
                            }
                        } else if (latest != null && latest.compareTo(lastUpdate) == 0) {
                            progressBar.setString("Your firmware is up to date");
                            progressBar.setIndeterminate(false);
                            processingTask = false;
                            progressBarTimer.restart();
                            if (verboseCheckUpdate) {
                                JOptionPane.showMessageDialog(parent,
                                        "Your Megadrum's firmware is up to date.\n(Latest version is " + latest + ")", "Information", JOptionPane.INFORMATION_MESSAGE);
                            }
                        } else if (latest != null && latest.compareTo(lastUpdate) < 0) {
                            progressBar.setString("Your firmware is up to date");
                            progressBar.setIndeterminate(false);
                            processingTask = false;
                            progressBarTimer.restart();
                            if (verboseCheckUpdate) {
                                JOptionPane.showMessageDialog(parent,
                                        "Incredible!! your firmware is newer than the one\n"
                                        + "in Megadrum Website. You are dmitri, aren't you? :D\n(Latest version is " + latest + ")", "Information", JOptionPane.INFORMATION_MESSAGE);
                            }
                        }
                    } catch (Exception e) {
                        log.error("While checking for firmware updates.<br>The error was: " + Log.getExceptionInfo(e));
                        progressBar.setString("Error checking for updates");
                        progressBar.setIndeterminate(false);
                        processingTask = false;
                        progressBarTimer.restart();
                        if (verboseCheckUpdate) {
                            JOptionPane.showMessageDialog(parent, "Error while checking for firmware updates:\n" + Log.getExceptionInfo(e),
                                    "Error", JOptionPane.ERROR_MESSAGE);
                        }
                    }
                    if (!verboseCheckUpdate) { //if called from the background...
                        //if enabled the loading at startup
                        if (loadAtStartupEnabled) {
                            loadAllSettingsFromMD();
                        }
                    }
                }
            }.start();
        }
    }

    //  WRITE IN EEPROM
    public void sendWriteOrderToMD() {
        if (!processingTask) {
            progressBarTimer.stop();
            processingTask = true;
            progressBar.setIndeterminate(true);
            progressBar.setString("Sending \"Write in Slot 1\" order");
            new Thread() {

                @Override
                public void run() {
                    if (MidiCommon.sendSysex(SysexRequest.writeEEPROMRequest(chainID), out)) {
                        log.info("Write in Slot 1 order sent to MD");
                    }
                    progressBar.setString("\"Write in Slot 1\" order sent");
                    progressBar.setIndeterminate(false);
                    processingTask = false;
                    progressBarTimer.restart(); //auto-remove message from progress bar after 5 seconds
                }
            }.start();
        }
    }

    // #######################
    // ### EVENT LISTENERS ###
    // #######################
    //list listener for changing between different pad panels
    public void valueChanged(ListSelectionEvent ev) {
        if (!padList.getValueIsAdjusting()) {
            int index;
            for (index = 0; index < padList.getModel().getSize(); index++) {
                if (padList.isSelectedIndex(index)) {
                    break;
                }
            }
            if (index < padList.getModel().getSize()) {
                padPanelContainer.removeAll();
                padPanelContainer.add(pad[index], "Center");
                padPanelContainer.updateUI();
            }
        }
    }

    //restart the liveEditing timer
    public void scheduleSending(int eventType) {
        liveEditingEventType = eventType;
        liveEditingTimer.restart();
    }

    public void scheduleSending(int eventType, int padNumber) {
        currentPadNumber = padNumber;
        scheduleSending(eventType);
    }

    //action listener for menu items
    public void actionPerformed(ActionEvent ev) {
        Object o = ev.getSource();

        //progressBar timer
        if (liveEditingEnabled && o.equals(liveEditingTimer)) {
            switch (liveEditingEventType) {
                case Common.MISC_EVENT:
                    sendMiscSettingsToMD();
                    break;
                case Common.HIHAT_EVENT:
                    sendHihatSettingsToMD();
                    break;
                case Common.PAD_EVENT:
                    sendPadSettingsToMD();
                    break;
            }

            //progressBar timer
        } else if (o.equals(progressBarTimer)) {
            progressBar.setString("");

            //exit app
        } else if (o.equals(menu_exit)) {
            closeApp();

            //about splashscreen
        } else if (o.equals(menu_about)) {
            new AboutDialog(this);

            //open help file
        } else if (o.equals(menu_help)) {
            Common.showInBrowser(Common.HELP_URL, this);

            //show which firmware is recommended for this version of MCT
        } else if (o.equals(menu_recommendedFirmware)) {
            JOptionPane.showMessageDialog(this,
                    "These are the recommended Megadrum firmwares\n"
                    + "to use with this version of Megadrum Config Tool:\n"
                    + Common.RECOMMENDED_FIRMWARE_VERSION,
                    "Information", JOptionPane.INFORMATION_MESSAGE);

            //options to configure application and windows to show
        } else if (o.equals(menu_options)) {
            new Options(this);
        } else if (o.equals(menu_midiLog)) {
            midiLog.setLocationRelativeTo(this);
            midiLog.setVisible(true);
        } else if (o.equals(menu_appLog)) {
            log.setLocationRelativeTo(this);
            log.setVisible(true);
        } else if (o.equals(menu_drummapEditor)) {
            drummapEditor.setLocationRelativeTo(this);
            drummapEditor.setVisible(true);
        } else if (o.equals(menu_aftertouchMapping)) {
            aftertouchMapping.setLocationRelativeTo(this);
            aftertouchMapping.setVisible(true);
        } else if (o.equals(menu_customCurves)) {
            customCurves.setLocationRelativeTo(this);
            customCurves.setVisible(true);

            //############################
            //  ACTIONS FOR ALL SETTINGS
            //############################

        } else if (o.equals(menu_loadFromFile)) {
            byte b[] = SysexFile.loadFile(this, "Load all settings from file", "syx");
            if (b != null) {
                if (b.length == 0 || !applyAllSettingsToGUI(b)) {
                    JOptionPane.showMessageDialog(this, "The file selected doesn't seem\nto be a valid all-settings file.",
                            "Warning", JOptionPane.WARNING_MESSAGE);
                }
            }

        } else if (o.equals(menu_saveToFile)) {
            byte[] b = getAllSettingsFromGUI();
            if (b != null && Common.ERROR == SysexFile.saveFile(this, "Save settings to a file", "syx", b)) {
                JOptionPane.showMessageDialog(this, "Error saving all-settings file", "Warning", JOptionPane.WARNING_MESSAGE);
            }

        } else if (o.equals(menu_loadFromMega) || o.equals(b_loadFromMega1)) {
            loadAllSettingsFromMD();
        } else if (o.equals(menu_sendToMega) || o.equals(b_sendToMega1)) {
            sendAllSettingsToMD();
        } else if (o.equals(menu_sendToMegaOrderWrite) || o.equals(b_sendToMegaOrderWrite1)) {
            sendAllSettingsToMDAndWriteToSlot1();

            //#############################
            //  ACTIONS FOR MISC SETTINGS
            //#############################

        } else if (o.equals(menu_loadMiscFromFile)) {
            byte b[] = SysexFile.loadFile(this, "Load misc settings from file", "syx");
            if (b != null) {
                if (b.length == 0 || !applyMiscSettingsToGUI(b)) {
                    JOptionPane.showMessageDialog(this, "The file selected doesn't seem\nto be a valid misc settings file.",
                            "Warning", JOptionPane.WARNING_MESSAGE);
                }
            }
        } else if (o.equals(menu_saveMiscToFile)) {
            byte b[] = getMiscSettingsFromGUI();
            if (b != null && Common.ERROR == SysexFile.saveFile(this, "Save misc settings to a file", "syx", b)) {
                JOptionPane.showMessageDialog(this, "Error saving misc settings file", "Warning", JOptionPane.WARNING_MESSAGE);
            }
        } else if (o.equals(menu_loadMiscFromMega) || o.equals(b_loadMiscFromMega1)) {
            loadMiscSettingsFromMD();
        } else if (o.equals(menu_sendMiscToMega) || o.equals(b_sendMiscToMega1)) {
            sendMiscSettingsToMD();

            //##############################
            //  ACTIONS FOR HIHAT SETTINGS
            //##############################

        } else if (o.equals(menu_loadHihatFromFile)) {
            byte b[] = SysexFile.loadFile(this, "Load hihat settings from file", "syx");
            if (b != null) {
                if (b.length == 0 || !applyHihatSettingsToGUI(b)) {
                    JOptionPane.showMessageDialog(this, "The file selected doesn't seem\nto be a valid hihat settings file.",
                            "Warning", JOptionPane.WARNING_MESSAGE);
                }
            }
        } else if (o.equals(menu_saveHihatToFile)) {
            byte b[] = getHihatSettingsFromGUI();
            if (b != null && Common.ERROR == SysexFile.saveFile(this, "Save hihat settings to a file", "syx", b)) {
                JOptionPane.showMessageDialog(this, "Error saving hihat settings file", "Warning", JOptionPane.WARNING_MESSAGE);
            }
        } else if (o.equals(menu_loadHihatFromMega) || o.equals(b_loadHihatFromMega1)) {
            loadHihatSettingsFromMD();
        } else if (o.equals(menu_sendHihatToMega) || o.equals(b_sendHihatToMega1)) {
            sendHihatSettingsToMD();

            //############################
            //  ACTIONS FOR PAD SETTINGS
            //############################

        } else if (o.equals(menu_loadPadFromFile)) {
            byte b[] = SysexFile.loadFile(this, "Load pad settings from file", "syx");
            if (b != null) {
                if (b.length == 0 || !applyPadSettingsToGUI(b)) {
                    JOptionPane.showMessageDialog(this, "The file selected doesn't seem\nto be a valid pad settings file.",
                            "Warning", JOptionPane.WARNING_MESSAGE);
                }
            }
        } else if (o.equals(menu_savePadToFile)) {
            byte b[] = getPadSettingsFromGUI();
            if (b != null && Common.ERROR == SysexFile.saveFile(this, "Save pad settings to a file", "syx", b)) {
                JOptionPane.showMessageDialog(this, "Error saving pad settings file", "Warning", JOptionPane.WARNING_MESSAGE);
            }
        } else if (o.equals(menu_loadPadFromMega) || o.equals(b_loadPadFromMega1)) {
            loadPadSettingsFromMD();
        } else if (o.equals(menu_sendPadToMega) || o.equals(b_sendPadToMega1)) {
            currentPadNumber = padList.getSelectedIndex();
            sendPadSettingsToMD();

            //#########################
            //  ACTIONS FOR DRUM MAPS
            //#########################

        } else if (o.equals(menu_loadDrumMap)) {
            if (Common.ERROR == loadDrumMap()) {
                JOptionPane.showMessageDialog(this, "Error loading Drum Map file", "Warning", JOptionPane.WARNING_MESSAGE);
            }
        } else if (o.equals(menu_saveDrumMap)) {
            if (Common.ERROR == saveDrumMap()) {
                JOptionPane.showMessageDialog(this, "Error saving Drum Map file", "Warning", JOptionPane.WARNING_MESSAGE);
            }

            //###################
            //  FIRMWARE
            //###################

            //update firmware
        } else if (o.equals(menu_updateFirmware)) {
            if (bootloader < 4) {
                byte b[] = SysexFile.loadFile(this, "Select firmware sysex file to send to Megadrum", "syx");
                if (b != null) {
                    if (b.length > 0 && b[0] == Common.SYSEX_BEGIN
                            && b[1] == Common.TYPE_FIRMWAREDATA
                            && b[b.length - 1] == Common.SYSEX_END) {
                        updateFirmware(b);
                    } else {
                        JOptionPane.showMessageDialog(this, "The file selected doesn't seem\nto be a valid firmware file.",
                                "Warning", JOptionPane.WARNING_MESSAGE);
                    }
                }
            } else {
                JOptionPane.showMessageDialog(this, "You have selected to update MegaDrum encrypted firmware\n"
                        + "with the new secure bootloader (v5). Please proceeed as follows:\n"
                        + "1. Read the following carefully and then close MCT;\n"
                        + "2. Once MCT is closed boot MegaDrum with the <Left> key pressed;\n"
                        + "3. Set your crystal frequency by pressing <Up>: (16->20->24->12);\n"
                        + "4. Start MCT and follow further instructions.",
                        "Warning", JOptionPane.WARNING_MESSAGE);
                updateOnRestart = true;
                updateConfigurationFile();
            }

            //modify firmware
        } else if (o.equals(menu_modifyFirmware)) {
            //select firmware file
            byte b[] = SysexFile.loadFile(this, "Select firmware sysex file to be modified with custom input names", "syx");
            if (b != null) {
                if (b.length > 0 && b[0] == Common.SYSEX_BEGIN
                        && b[1] == Common.TYPE_FIRMWAREDATA
                        && b[b.length - 1] == Common.SYSEX_END) {
                    //if firmware file correctly read, show input names conversion table
                    new EditPadAndInputNames(this);
                    //after the conversion table is closed, create new modified firmware
                    byte[] newdata = replaceInputNames(b);
                    if (newdata != null) {
                        //tell the user to save the new firmware file
                        SysexFile.saveFile(this, "Save new modified firmware sysex to a file", "syx", newdata);
                    } else {
                        JOptionPane.showMessageDialog(this, "There was an error trying to\nreplace input names with custom ones.",
                                "Error", JOptionPane.ERROR_MESSAGE);
                    }
                } else {
                    JOptionPane.showMessageDialog(this, "The file selected doesn't seem\nto be a valid firmware file.",
                            "Warning", JOptionPane.WARNING_MESSAGE);
                }
            }

            //change input names
        } else if (o.equals(menu_inputNames)) {
            new EditPadAndInputNames(this);

            //check for updates
        } else if (o.equals(menu_checkUpdate)) {
            checkForFirmwareUpdates(true);

            //###################
            //  WRITE IN EEPROM
            //###################

        } else if (o.equals(menu_orderWrite) || o.equals(b_orderWrite)) {
            sendWriteOrderToMD();

            //###################
            //  SET-ALL BUTTONS
            //###################

            //buttons for setting all fields at a time (special fields...)
        } else if (o.equals(b_dualHead)) {
            int value = ((CustomSpinnerModel) dualHead.getModel()).getIntValue();
            switch (value) {
                case 0: //2xMono
                    for (int i = numberMonoPads; i < pad.length; i++) {
                        pad[i].setValue(Common.HEAD, Common.DUALHEADTYPE, 0);
                        pad[i].setValue(Common.RIM, Common.DUALHEADTYPE, 0);
                    }
                    break;
                case 1: //P/P
                    for (int i = numberMonoPads; i < pad.length; i++) {
                        pad[i].setValue(Common.HEAD, Common.DUALHEADTYPE, 1);
                        pad[i].setValue(Common.RIM, Common.DUALHEADTYPE, 0);
                    }
                    break;
                case 2: //P/S
                    for (int i = numberMonoPads; i < pad.length; i++) {
                        pad[i].setValue(Common.HEAD, Common.DUALHEADTYPE, 1);
                        pad[i].setValue(Common.RIM, Common.DUALHEADTYPE, 1);
                    }
                    break;
            }
        } else if (o.equals(b_bNote)) {
            int value = ((CustomSpinnerModel) bNote.getModel()).getIntValue();
            for (int i = numberMonoPads; i < pad.length; i++) {
                pad[i].setValue(Common.THIRDZONE, Common.BNOTE, value);
            }
        } else if (o.equals(b_bThreshold)) {
            int value = ((CustomSpinnerModel) bThreshold.getModel()).getIntValue();
            for (int i = numberMonoPads; i < pad.length; i++) {
                pad[i].setValue(Common.THIRDZONE, Common.BTHRESHOLD, value);
            }

            //LAST CONDITION: rest of buttons for setting all fields at a time
        } else {
            int field = 0;
            int value = 0;
            if (o.equals(b_note)) {
                field = Common.NOTE;
                value = ((CustomSpinnerModel) note.getModel()).getIntValue();
            } else if (o.equals(b_channel)) {
                field = Common.CHANNEL;
                value = ((CustomSpinnerModel) channel.getModel()).getIntValue();
            } else if (o.equals(b_curve)) {
                field = Common.CURVE;
                value = ((CustomSpinnerModel) curve.getModel()).getIntValue();
            } else if (o.equals(b_comprLvl)) {
                field = Common.COMPRLVL;
                value = ((CustomSpinnerModel) comprLvl.getModel()).getIntValue();
            } else if (o.equals(b_lvlShift)) {
                field = Common.LVLSHIFT;
                value = ((CustomSpinnerModel) lvlShift.getModel()).getIntValue();
            } else if (o.equals(b_xtalk)) {
                field = Common.XTALK;
                value = ((CustomSpinnerModel) xtalk.getModel()).getIntValue();
            } else if (o.equals(b_xtalkGrp)) {
                field = Common.XTALKGRP;
                value = ((CustomSpinnerModel) xtalkGrp.getModel()).getIntValue();
            } else if (o.equals(b_threshold)) {
                field = Common.THRESHOLD;
                value = ((CustomSpinnerModel) threshold.getModel()).getIntValue();
            } else if (o.equals(b_gain)) {
                field = Common.GAIN;
                value = ((CustomSpinnerModel) gain.getModel()).getIntValue();
            } else if (o.equals(b_hiLvlAuto)) {
                field = Common.HILVLAUTO;
                value = ((CustomSpinnerModel) hiLvlAuto.getModel()).getIntValue();
            } else if (o.equals(b_highLvl)) {
                field = Common.HIGHLVL;
                value = ((CustomSpinnerModel) highLvl.getModel()).getIntValue();
            } else if (o.equals(b_retrigger)) {
                field = Common.RETRIGGER;
                value = ((CustomSpinnerModel) retrigger.getModel()).getIntValue();
            } else if (o.equals(b_dynLevel)) {
                field = Common.DYNLEVEL;
                value = ((CustomSpinnerModel) dynLevel.getModel()).getIntValue();
            } else if (o.equals(b_dynTime)) {
                field = Common.DYNTIME;
                value = ((CustomSpinnerModel) dynTime.getModel()).getIntValue();
            } else if (o.equals(b_minScan)) {
                field = Common.MINSCAN;
                value = ((CustomSpinnerModel) minScan.getModel()).getIntValue();
            }
            //firstly set parameter in the mono pads
            for (int i = 0; i < numberMonoPads; i++) {
                pad[i].setValue(Common.MONO, field, value);
            }
            //second, set parameter in stereo pads
            for (int i = numberMonoPads; i < pad.length; i++) {
                pad[i].setValue(Common.HEAD, field, value);
                pad[i].setValue(Common.RIM, field, value);
            }
        }
    }

    // ##########################
    // ### GETTER AND SETTERS ###
    // ##########################
    //GET and SET methods
    public int getCurrentSelectedPad() {
        return padList.getSelectedIndex();
    }

    public int getChainID() {
        return chainID;
    }

    public void setChainID(int i) {
        chainID = i;
    }

    public int getNumberOfInputs() {
        return numberInputs;
    }

    public void setNumberOfInputs(int n) {
        numberInputs = n - 1; //substract the hihat pedal input
        calculateNumberOfPads();
    }

    public int getNumberOfMonoPads() {
        return numberMonoPads;
    }

    /*public void setNumberOfMonoPads(int n) {
    numberMonoPads = n;
    }*/
    public int getNumberOfStereoPads() {
        return numberStereoPads;
    }

    /*public void setNumberOfStereoPads(int n) {
    numberStereoPads = n;
    }*/
    public String getClock() {
        return clock;
    }

    public int getBootloader() {
        return bootloader;
    }

    public void setClock(String s) {
        clock = s;
    }

    public void setBootloader(int b) {
        bootloader = b;
    }

    public String getMCU() {
        return MCU;
    }

    public void setMCU(String s) {
        MCU = s;
    }

    public int getTheme() {
        return theme;
    }

    public void setTheme(int i) {
        if (i >= 0 && i <= 19) {
            theme = i;
        }
    }

    public int getLookAndFeel() {
        return lookandfeel;
    }

    public void setLookAndFeel(int i) {
        if (i >= 0 && i <= 6) {
            lookandfeel = i;
        }
    }

    public int getRimSwitchThreshold() {
        return rimSwitch;
    }

    public void setRimSwitchThreshold(int i) {
        rimSwitch = i;
    }

    public boolean getQuickAccessEnabled() {
        return quickAccessEnabled;
    }

    public void setQuickAccessEnabled(boolean i) {
        quickAccessEnabled = i;
    }

    public boolean getLiveEditingEnabled() {
        return liveEditingEnabled;
    }

    public void setLiveEditingEnabled(boolean i) {
        liveEditingEnabled = i;
    }

    public boolean getLoadAtStartupEnabled() {
        return loadAtStartupEnabled;
    }

    public void setLoadAtStartupEnabled(boolean i) {
        loadAtStartupEnabled = i;
    }

    public int getQuickAccessPadThreshold() {
        return quickAccessPad;
    }

    public void setQuickAccessPadThreshold(int i) {
        quickAccessPad = i;
    }

    public int getQuickAccessPedalThreshold() {
        return quickAccessPedal;
    }

    public void setQuickAccessPedalThreshold(int i) {
        quickAccessPedal = i;
    }

    public int getSysexWaitTime() {
        return sysexWaitTime;
    }

    public void setSysexWaitTime(int i) {
        sysexWaitTime = i;
    }

    public PadPanel getPad(int i) {
        return pad[i];
    }

    public String getInputDeviceName() {
        return inputDeviceName;
    }

    public String getOutputDeviceName() {
        return outputDeviceName;
    }

    public String getThroughDeviceName() {
        return throughDeviceName;
    }

    public void setInputDeviceName(String s) {
        boolean needsRestart = false;
        if (s != null && inputDeviceName != null && !s.equals(inputDeviceName)) {
            needsRestart = true;
        }
        inputDeviceName = s;
        if (needsRestart && !restartInput()) {
            JOptionPane.showMessageDialog(this, "Unable to open MIDI IN device.\n"
                    + "Please select a different Midi Device\nor close this app and try again.",
                    "Error", JOptionPane.ERROR_MESSAGE);
        }
    }

    public void setOutputDeviceName(String s) {
        boolean needsRestart = false;
        if (s != null && outputDeviceName != null && !s.equals(outputDeviceName)) {
            needsRestart = true;
        }
        outputDeviceName = s;
        //restar midi output device
        if (needsRestart && !restartOutput()) {
            JOptionPane.showMessageDialog(this, "Unable to open MIDI OUT device.\n"
                    + "Please select a different Midi Device\nor close this app and try again.",
                    "Error", JOptionPane.ERROR_MESSAGE);
        }
    }

    public void setThroughDeviceName(String s) {
        if (s != null) {
            boolean needsRestart = false;
            if (s != null && throughDeviceName != null && !s.equals(throughDeviceName)) {
                needsRestart = true;
            }
            throughDeviceName = s;
            //restar midi output device
            if (needsRestart && !restartThrough()) {
                JOptionPane.showMessageDialog(this, "Unable to open MIDI THROUGH device.\n"
                        + "Please select a different Midi Device\nor close this app and try again.",
                        "Error", JOptionPane.ERROR_MESSAGE);
            }
            //if device name is null that means we want to disable midi through
        } else {
            throughDeviceName = null;
            stopThrough();
        }
    }

    public String getPadName(int i) {
        return padName[i];
    }

    public String getInputName(int i) {
        return inputName[i];
    }

    public void setPadName(int i, String name) {
        if (i >= 0 && i < numberMonoPads + numberStereoPads) {
            if (name == null) {
                name = Common.PAD_NAMES[i]; //if null, convert to empty string
            }
            padName[i] = name;
        }
    }

    public void updatePadListNames() {
        if (padList != null) {
            padList.setListData(padName);
        }
    }

    public void setInputName(int i, String name) {
        if (i >= 0 && i < numberInputs) {
            if (name == null) {
                name = Common.SUBPAD_NAMES[i];
            }
            if (name.length() < Common.SUBPAD_NAME_LENGTHS[i]) {
                //fill with spaces
                int nameLength = name.length();
                for (int j = 0; j < Common.SUBPAD_NAME_LENGTHS[i] - nameLength; j++) {
                    name = name + " ";
                }
            } else if (name.length() > Common.SUBPAD_NAME_LENGTHS[i]) {
                //get substring
                name = name.substring(0, Common.SUBPAD_NAME_LENGTHS[i]);
            }
            inputName[i] = name;

            //update names if already loaded
            if (pad != null) {
                if (i < numberMonoPads) { //MONO
                    pad[i].setSubpadName(Common.MONO, inputName[i]);
                } else if ((i - numberMonoPads) % 2 == 0) { //STEREO-HEAD
                    pad[numberMonoPads + (i - numberMonoPads) / 2].setSubpadName(Common.HEAD, inputName[i]);
                } else { //STEREO-RIM
                    pad[numberMonoPads + (i - numberMonoPads - 1) / 2].setSubpadName(Common.RIM, inputName[i]);
                }
            }
        }
    }

    // ###############
    // ### RUN APP ###
    // ###############
    public static void main(String args[]) {
        //init app
        java.awt.EventQueue.invokeLater(new Runnable() {

            public void run() {
                //first create application log for logging purposes
                MegadrumConfigTool.log = new ApplicationLog();
                new MegadrumConfigTool(true);
            }
        });
    }

    // #################
    // ### CLOSE APP ###
    // #################
    @Override
    public void dispose() {
        //close Midi devices
        uninitializeMidiDevices();
        //dispose JFrame
        super.dispose();
    }

    // Method called when you press the X to close the application
    private void closeApp() {
        if (!closingApp) {
            //close application
            closingApp = true;

            new Thread() {

                @Override
                public void run() {
                    int result;
                    result = JOptionPane.showConfirmDialog(parent,
                            "Do you want to send current settings to Megadrum\n"
                            + "and save them into Slot 1 before exiting?",
                            "Information", JOptionPane.YES_NO_CANCEL_OPTION);
                    //user cancelled the action of closing?
                    if (result == JOptionPane.CANCEL_OPTION) {
                        closingApp = false;
                        return; //don't close application
                    }
                    //save state of the window (maximized or not?)
                    windowState = getExtendedState();
                    updateConfigurationFile();
                    //before closing save all settings to default-config.syx file
                    byte[] b = getAllSettingsFromGUI();
                    if (b != null) {
                        SysexFile.saveFile(b, Common.getUserProfileFolder() + Common.DEFAULT_CONFIG_FILE);
                    }
                    //if user wants to send settings to MD, send them and wait for them to be sent
                    if (result == JOptionPane.YES_OPTION) {
                        processingTask = false; //set this to false, so it doesn't matter a task is already running
                        //this task will be executed anyway
                        sendAllSettingsToMDAndWriteToSlot1();
                        //wait for sending to finish
                        synchronized (closeMonitor) {
                            try {
                                closeMonitor.wait();
                            } catch (InterruptedException e) {
                            }
                        }
                    }
                    //show info to the user
                    progressBar.setString("Now closing...");
                    //exit
                    dispose();
                    System.exit(0);
                }
            }.start();
        }
    }
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JPanel applicationPanel;
    private javax.swing.JSpinner bNote;
    private javax.swing.JSpinner bThreshold;
    private javax.swing.JButton b_bNote;
    private javax.swing.JButton b_bThreshold;
    private javax.swing.JButton b_channel;
    private javax.swing.JButton b_comprLvl;
    private javax.swing.JButton b_curve;
    private javax.swing.JButton b_dualHead;
    private javax.swing.JButton b_dynLevel;
    private javax.swing.JButton b_dynTime;
    private javax.swing.JButton b_gain;
    private javax.swing.JButton b_hiLvlAuto;
    private javax.swing.JButton b_highLvl;
    private javax.swing.JButton b_loadFromMega1;
    private javax.swing.JButton b_loadHihatFromMega1;
    private javax.swing.JButton b_loadMiscFromMega1;
    private javax.swing.JButton b_loadPadFromMega1;
    private javax.swing.JButton b_lvlShift;
    private javax.swing.JButton b_minScan;
    private javax.swing.JButton b_note;
    private javax.swing.JButton b_orderWrite;
    private javax.swing.JButton b_retrigger;
    private javax.swing.JButton b_sendHihatToMega1;
    private javax.swing.JButton b_sendMiscToMega1;
    private javax.swing.JButton b_sendPadToMega1;
    private javax.swing.JButton b_sendToMega1;
    private javax.swing.JButton b_sendToMegaOrderWrite1;
    private javax.swing.JButton b_threshold;
    private javax.swing.JButton b_xtalk;
    private javax.swing.JButton b_xtalkGrp;
    private javax.swing.JSpinner channel;
    private javax.swing.JSpinner comprLvl;
    private javax.swing.JSpinner curve;
    private javax.swing.JSpinner dualHead;
    private javax.swing.JSpinner dynLevel;
    private javax.swing.JSpinner dynTime;
    private javax.swing.JSpinner gain;
    private javax.swing.JPanel graphicalAidPanel;
    private javax.swing.JSpinner hiLvlAuto;
    private javax.swing.JSpinner highLvl;
    private javax.swing.JPanel hihatPanelContainer;
    private javax.swing.JTextArea infoTextArea;
    private javax.swing.JPanel logoContainer;
    private javax.swing.JSpinner lvlShift;
    private javax.swing.JMenuItem menu_about;
    private javax.swing.JMenuItem menu_aftertouchMapping;
    private javax.swing.JMenuItem menu_appLog;
    private javax.swing.JMenuItem menu_checkUpdate;
    private javax.swing.JMenuItem menu_customCurves;
    private javax.swing.JMenuItem menu_drummapEditor;
    private javax.swing.JMenuItem menu_exit;
    private javax.swing.JMenuItem menu_help;
    private javax.swing.JMenuItem menu_inputNames;
    private javax.swing.JMenuItem menu_loadDrumMap;
    private javax.swing.JMenuItem menu_loadFromFile;
    private javax.swing.JMenuItem menu_loadFromMega;
    private javax.swing.JMenuItem menu_loadHihatFromFile;
    private javax.swing.JMenuItem menu_loadHihatFromMega;
    private javax.swing.JMenuItem menu_loadMiscFromFile;
    private javax.swing.JMenuItem menu_loadMiscFromMega;
    private javax.swing.JMenuItem menu_loadPadFromFile;
    private javax.swing.JMenuItem menu_loadPadFromMega;
    private javax.swing.JMenuItem menu_midiLog;
    private javax.swing.JMenuItem menu_modifyFirmware;
    private javax.swing.JMenuItem menu_options;
    private javax.swing.JMenuItem menu_orderWrite;
    private javax.swing.JMenuItem menu_recommendedFirmware;
    private javax.swing.JMenuItem menu_saveDrumMap;
    private javax.swing.JMenuItem menu_saveHihatToFile;
    private javax.swing.JMenuItem menu_saveMiscToFile;
    private javax.swing.JMenuItem menu_savePadToFile;
    private javax.swing.JMenuItem menu_saveToFile;
    private javax.swing.JMenuItem menu_sendHihatToMega;
    private javax.swing.JMenuItem menu_sendMiscToMega;
    private javax.swing.JMenuItem menu_sendPadToMega;
    private javax.swing.JMenuItem menu_sendToMega;
    private javax.swing.JMenuItem menu_sendToMegaOrderWrite;
    private javax.swing.JMenuItem menu_updateFirmware;
    private javax.swing.JPanel midiBarsContainer;
    private javax.swing.JSpinner minScan;
    private javax.swing.JPanel miscPanelContainer;
    private javax.swing.JSpinner note;
    private javax.swing.JList padList;
    private javax.swing.JPanel padPanelContainer;
    private javax.swing.JPanel parameterLabels;
    private javax.swing.JProgressBar progressBar;
    private javax.swing.JSpinner retrigger;
    private javax.swing.JMenu supermenu_drummaps;
    private javax.swing.JMenu supermenu_firmware;
    private javax.swing.JMenu supermenu_view;
    private javax.swing.JPanel taskPanel;
    private javax.swing.JSpinner threshold;
    private javax.swing.JSpinner xtalk;
    private javax.swing.JSpinner xtalkGrp;
    // End of variables declaration//GEN-END:variables
    //this
    private MegadrumConfigTool parent = this;
    //midi devices
    private MidiDevice in, out, through;
    private Receiver throughReceiver;
    private Transmitter transmitter;
    private MultiReceiver multiReceiver;
    //object to synchronize closing of application with sending of all settings
    private final Object closeMonitor = new Object();
    //application state
    private int windowState = JFrame.NORMAL;
    private boolean closingApp = false;
    private boolean processingTask = false;
    private boolean verboseCheckUpdate;
    private boolean verboseFirmwareInfo;
    private int currentPadNumber; //set this value before calling sendPadSettingsToMD
    //variables for live editing
    private Timer liveEditingTimer;
    private int liveEditingEventType;
    //variables for task threads
    private Timer progressBarTimer;
    private byte[] tempData = null; //store any data you want to send through a task
    private int tempInt = 0; //store any integer you want to send through a task
    //set to true when application starts for the first time in this computer
    private boolean firstRun;
    //set to true, when an update on reboot has been requested
    private boolean updateOnRestart;
    //configuration variables
    private int numberInputs, numberMonoPads, numberStereoPads;
    private String clock;
    private String MCU;
    private int chainID, bootloader, theme, lookandfeel, quickAccessPad, quickAccessPedal, pedalPosition, rimSwitch, sysexWaitTime;
    private String inputDeviceName;
    private String outputDeviceName;
    private String throughDeviceName;
    private String[] inputName;
    private String[] padName;
    private String lastUpdate;
    private boolean quickAccessEnabled;
    private boolean liveEditingEnabled;
    private boolean loadAtStartupEnabled;
    //panels
    private HihatPanel hihat;
    private PadPanel[] pad;
    private MiscPanel misc;
    private MidiMonitor midiMonitor;
    private MidiLog midiLog;
    private CustomCurvesEditor customCurves;
    private DrummapEditor drummapEditor;
    private AftertouchMapping aftertouchMapping;
    public static ApplicationLog log;
    public static final int Error_OK = 0x11;
    public static final int Error_CRC = 0x22;
    public BinFile mdUpdateData;
}
